本文將一步步介紹如何使用React或anu創建 一個彈出層。
React時代,代碼都是要經過編譯的,我們很多時間都耗在babel與webpack上。因此本文也介紹如何玩webpack與babel。
我們創建一個ui目錄,里面添加一個package.json。內容如下,里面已經是盡量減少babel插件的使用了。
{
"name": "ui",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "RubyLouvre",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "^6.4.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.16.0",
"webpack": "^2.2.1"
},
"dependencies": {
"prop-types": "^15.5.10",
"anujs": "^1.0.0"
}
}
如果你不想用anu,可以改成react與react-dom。
"dependencies": {
"prop-types": "^15.5.10"
"react": "^15.5.4",
"react-dom": "^15.5.4"
}
anu本身沒有propTypes,而react最近的版本也把propTypes拆了出來,因此我們需要獨立安裝prop-types這個包。
webpack我們緊隨時髦,使用2.0, 而babel則是一大堆東西。
然后我們在控制台npm install
敲一下,會給我們安裝上幾屏的依賴,下面只是展示了一部分。可見前端的發展多么可怕,以前只是幾個JS文件就覺得非常臃腫了,現在幾百個習以為常。盡管它們大部分是預處理JS的。這也為React帶來巨大的門檻,門檻越高,工資越高。
然后 ui目錄下建立一個src目錄,里面建toast.js。
//第一部分,引入依賴與定義模塊內的全局變量
import React,{Component} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
let singleton = null;
const container = document.createElement('div'),
defaultProps = {
show: false
},
propTypes = {
/**
* @property show
* @description 是否顯示,默認false
* @type Boolean
* @default false
* @skip
*/
show: PropTypes.bool
};
document.body.appendChild(container);
//第二部分,定義組件
class ToastReact extends Component {
constructor(props) {
super(props);
this.state = {
show: this.props.show,
content: '',
autoHideTime: 2000
};
this._timer = null;
singleton = this;
}
shouldComponentUpdate(nextProps, nextState) {
this.setState({ show: nextState.show });
if (!!this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
this._timer = setTimeout(() => this.setState({ show: false }), nextState.autoHideTime);
return true;
}
componentWillUnmount() {
clearTimeout(this._timer);
document.body.removeChild(container);
}
render() {
const { show, content } = this.state;
return (
<div
className="yo-toast"
style={{
display: show ? null : 'none'
}}
>{content}</div>
);
}
}
ToastReact.propTypes = propTypes;
ToastReact.defaultProps = defaultProps;
ReactDOM.render(<ToastReact />, container);
// 第三部分,一個代理對象,設置Toast的顯示隱藏函數
/**
* Toast顯隱函數
* @returns {Object}
*/
export default {
/**
* @method show
* @type Function
* @description 打開組件,顯示傳入的內容
* @param {String} content 組件顯示的內容
* @param {Number} [autoHideTime] 內容顯示的持續時間,默認2000ms
*/
show(content = 'no content', autoHideTime = 2000) {
singleton.setState({
content,
autoHideTime,
show: true
});
return this;
},
/**
* @method hide
* @type Function
* @description 關閉正在顯示的組件
*/
hide() {
singleton.setState({ show: false });
return this;
}
};
整個文件分三部分,大家認真看注釋。之所以將彈層變成單例模式,因此窗口通常只存在一個彈層。存在多個彈層的情況,大多數是設計不合理吧,比如說層上層。這種由於在React時代,數據都保存在redux中,因此當層上層出現時,原彈層的用法輸入可以保存到redux中,然后再改變彈層的內容,就可以實現層上層的功能了。在jQuery時代,數據固化在dom里,無法剝離,才出現這奇葩的情況。
然后 我們再建一個app.js,里面模擬業務線的同學使用toast吧。
import Tooltip from './coast';
var btn = document.querySelectorAll('.demo');
btn[0].addEventListener('click', function() {
console.log('tooltip');
Tooltip.show('the tooltip autoHide after 2s');
}, false);
btn[1].addEventListener('click', function() {
console.log('tooltip 2')
Tooltip.show('the tooltip autoHide after 3s', 3000);
}, false);
btn[2].addEventListener('click', function() {
console.log('tooltip 3')
var tip = Tooltip.show('the tooltip will be hidden before the default time 2s');
setTimeout(()=>tip.hide(), 1000);
}, false);
然后在src的上一級目錄,即我們原來ui 目錄建一個index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tooltip demo</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<style>
.yo-toast {
position: fixed;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
min-width: 1rem;
max-width: 90%;
z-index: 1500;
height: 3rem;
padding: 20px 10px;
border-radius: .05rem;
background-color: rgba(0, 0, 0, .5);
color: #fff;
}
</style>
</head>
<body>
<button>normal tooltip</button>
<button>autohideTime tooltip</button>
<button>hide() tooltip</button>
</body>
</html>
我們准備在這個文件里引用我們的JS。JS必須要打包過的,babel處理過的。因此下面是重頭戲,建立一個webpack.config.js
const webpack = require("webpack");
const path = require("path");
const fs = require("fs");
module.exports = {
context: __dirname,
entry: {
app: "./src/app.js"
},
output: {
path: __dirname + "/dist/",
filename: "[name].js"
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
options: {
presets: ["es2015", "react"]
},
exclude: path.resolve(__dirname, "node_modules")
}
]
},
resolve: {
//如果不使用anu,就可以把這里注釋掉
alias: {
react: "anujs/dist/React.js",
react: "anujs/dist/React.js",
"react-dom": "anujs/dist/React.js"
}
}
};
我們需要在resolve配置項上設置別名,anu一個JS文件就包含了react與react-dom的功能,體積又少,最適合線上使用。
我們敲下webpack命令,就發生成一個dist/app.js文件,然后 在index.html上引用它
<script src="./dist/app.js"></script>
最終效果圖
本來還想把業務中用到的babel helpers分離出來,最后可恥的失敗了,誰會麻煩給個參考項目給我。