很久以前搭建的一個react執行環境,受歷史影響是webpack3.10.0和webpack-dev-server2.7.1的環境,新項目準備用webpack4從新弄弄了,舊的記錄就合併發佈了(在沒有快速目錄的地方寫的,以前是分爲了10個文件)css
所使用的一些包可能進過不斷的迭代升級已不支持html
01.初始化項目(安裝須要的包)
//生成package.json npm init
安裝基礎包
npm install react react-dom --save npm install webpack@3.10.0 webpack-dev-server@2.7.1 --save-dev <!--webpack和webpack-dev-server同時全局安裝一下--> npm install webpack@3.10.0 wepack-dev-server@2.7.1 -g
webpack-dev-server@2.7.3是最後兼容ie9的版本,升級以後會致使打包的文件在ie9下運行報錯node
安裝和配置babel
npm install babel-core 核心功能 babel-loader babel-polyfill 轉換低版本代碼 babel-preset-env 解析Es6 babel-preset-react 解析JSX --save-dev
安裝loader包
npm install css-loader style-loader file-loader url-loader image-webpack-loader html-loader --save-dev
- --save 會把依賴包名稱添加到 package.json 文件 dependencies
dependencies是運行時依賴,是咱們發佈後還須要依賴的模塊react
- --save-dev 則添加到 package.json 文件 devDependencies
devDependencies是開發時的依賴,在發佈後用不到它,而只是在咱們開發纔用到它jquery
當npm install時,會下載dependencies和devDependencies中的模塊,當使用npm install –production或者註明NODE_ENV變量值爲production時,只會下載dependencies中的模塊webpack
02.執行程序,建立文件
建立/.babelrc
//.babelrc { "presets": [ "react", "env" ] }
建立webpack.config.js
const path = require('path'); module.exports = { context: path.resolve(__dirname, 'app'), resolve: { extensions: ['.js', '.jsx'], //後綴名自動補全,可使用jsx的文件後綴 modules: [ path.resolve(__dirname, 'node_modules') ] }, entry: './app.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.js', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['env', 'react'], } } }, { test: /index\.html/, use: [{ loader: 'file-loader', options: { name (file) { return 'index.[ext]' } } }] } ] }, };
建立/app/component/App.js
import React from 'react'; import Header from './Header'; import Content from './Content'; import Sidebar from './Sidebar'; class App extends React.Component { render() { return ( <div className="app"> <Header/> <Sidebar/> <Content/> </div> ) } } export default App;
建立/app/component/Header.js
import React from 'react'; class Header extends React.Component { render() { return ( <div>this is header</div> ) } } export default Header;
建立/app/component/Sidebar.js
import React from 'react'; class Sidebar extends React.Component { render() { return ( <div className="sidebar"> this is sidebar </div> ) } } export default Sidebar;
建立/app/component/Content.js
import React from 'react'; class Content extends React.Component { render() { return ( <div className="content"> this is content </div> ) } } export default Content;
建立/app/app.js入口文件
import './index.html'; import React from 'react'; import ReactDom from 'react-dom'; import App from './component/App'; ReactDom.render( <App/>, document.getElementById('root') );
建立/app/index.html
<!DOCTYPE html> <html lang="en"> <head> <base href="/"> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <div id="root"></div> <script src="./app.js"></script> </body> </html>
執行webpack打包
<!--控制檯執行webpack會在/dist中生成一個app.js和index.html--> webpack
開啓本地服務器
修改webpack.config.js
//webpack-dev-server配置 devServer: { contentBase: './dist', historyApiFallback: true, inline: true,//源文件改變,會自動刷新頁面 port: 1234,//設置默認監聽端口,若是省略,默認爲"8080" },
修改package.json
"scripts": { "start": "webpack-dev-server" },
執行start
npm start
03.添加css樣式
新建/style/app.css
html, body, #root, .app { width: 100%; height: 100%; margin: 0; padding: 0; } div { box-sizing: border-box; } .header { position: fixed; top: 0; left: 150px; right: 0; padding: 0 30px; height: 61px; background: #ebebeb; } .title { width: 90%; margin: 0 5%; height: 80px; line-height: 80px; color: #fff; font-size: 20px; text-align: center; border-bottom: 1px solid rgba(255, 255, 255, 0.2); } .sidebar { position: fixed; top: 0; bottom: 0; left: 0; width: 150px; background: #3d3c4a; } .header-con { position: relative; width: 100%; height: 60px; } .user { position: absolute; right: 0; top: 15px; height: 30px; line-height: 30px; color: gray; } .text { float: left; height: 30px; } .avatar { float: right; width: 30px; height: 30px; } .avatar img { width: 100%; height: 100%; border-radius: 90%; } .content { position: fixed; top: 60px; left: 150px; right: 0; bottom: 0; padding: 30px; color: gray; }
修改/app/component/Header.js
import React from 'react'; class Header extends React.Component { render() { return ( <div className="header"> <div className="header-con"> <div className="user"> 歡迎您! </div> </div> </div> ) } } export default Header;
修改/app/component/Sidebar.js
import React from 'react'; class Sidebar extends React.Component { render() { return ( <div className="sidebar"> <div className="title"> easterCat </div> </div> ) } } export default Sidebar;
修改/app/component/App.js
... import '../style/app.css';
添加css依賴/webpack.config.js
module: { rules: [ { test: /\.css$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], }, ] },
打包運行
webpack
此時的css打包是自動添加到index的style標籤裏面git
04.添加圖片的依賴
建立/app/images/avatar.jpg
修改/app/component/Header.js
import React from 'react'; import Avatar from '../images/avatar.jpg'; class Header extends React.Component { render() { return ( <div className="header"> <div className="header-con"> <div className="user"> <span className="text">歡迎您!</span> <span className="avatar"> <img src={Avatar} alt="" /> </span> </div> </div> </div> ) } } export default Header;
添加圖片依賴/webpack.config.js
module: { rules: [ { test: /.*\.(gif|png|jpe?g|svg)$/, use: [{ loader: 'url-loader', options: { limit: 10000, name: 'images/[name].[ext]' } }, { loader: 'image-webpack-loader' }] }, ] },
05.添加預編譯語言-sass
<!--less和sass都用,此處用sass--> npm install sass-loader node-sass --save-dev
修改webpack.config.js
module: { rules:[{ test: /\.scss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true, outputStyle: 'expanded', sourceMapContents: true } }] },] },
添加app.scss
@import 'body'; @import 'header'; @import 'sidebar'; @import 'content';
將app.css拆分紅body.scss/header.scss/sidebar.scss/content.scss
<!--例:header.scss--> .header { position: fixed; top: 0; left: 150px; right: 0; padding: 0 30px; height: 61px; background: #ebebeb; .user { position: absolute; right: 0; top: 15px; height: 30px; line-height: 30px; color: gray; } .header-con { position: relative; width: 100%; height: 60px; } .text { float: left; height: 30px; } .avatar { float: right; width: 30px; height: 30px; img { width: 100%; height: 100%; border-radius: 90%; } } }
修改/app/component/app.js
import '../style/app.css'; <!--修改成--> import '../style/app.scss';
從新打包
webpack
安裝第二個預編譯語言less
npm install less less-loader --save-dev
修改webpack.config.js
module: { rules: [ { test: /\.less$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'less-loader', options: { sourceMap: true, outputStyle: 'expanded', sourceMapContents: true, } }] }, ] },
06.添加一個ui框架antd-design
antd-designgithub
npm install antd --save <!--babel-plugin-import必需要裝--> npm install babel-plugin-import --save-dev
引入antd樣式
<!--在App.js中引入--> import 'antd/dist/antd.css' 或 import 'antd/dist/antd.less' <!--在app.scss中引入--> @import "~antd/dist/antd.css";
使用組件,修改content.js
import React from 'react'; import Table from 'antd/lib/Table'; class Content extends React.Component { render() { const columns = [{ title: '姓名', dataIndex: 'name', key: 'name', }, { title: '年齡', dataIndex: 'age', key: 'age', }, { title: '電話號碼', dataIndex: 'number', key: 'number', }, { title: '郵箱', dataIndex: 'email', key: 'email', }]; const data = []; for (let i = 1; i < 15; i++) { let obj = { name: 'doudou', age: 32, number: 123456789, email: '123456789@163.com', }; obj.key = i; data.push(obj); } return ( <div className="content"> <Table columns={columns} dataSource={data}/> </div> ) } } export default Content;
babel-plugin-import插件
從 antd 引入模塊便可,無需引入樣式,babel-plugin-import 會加載 JS 和 CSSweb
修改.babelrc
//.babelrc { "plugins": [ [ "import", { "libraryName": "antd", "style": true //引入less,若是使用css文件就改成'css' } ] ] }
修改/app/component/Content.js
import {Table} from 'antd';
此時app.scss和App.js中引入的antd.css能夠去掉了npm
問題
使用antd-design
更改/app/component/App.js
import React from 'react'; import Header from './layout/Header'; import Content from './layout/Content'; import Sidebar from './layout/Sidebar'; import '../style/app.scss'; import {Layout} from 'antd'; class App extends React.Component { constructor() { super(); this.state = { collapsed: false }; this.toggle = () => { this.setState({ collapsed: !this.state.collapsed }) }; } render() { return ( <Layout className="layout-app"> <Layout.Sider trigger={null} collapsible collapsed={this.state.collapsed} > <Sidebar/> </Layout.Sider> <Layout> <Layout.Header style={{background: '#fff', padding: 0}}> <Header collapsed={this.state.collapsed} toggle={this.toggle} /> </Layout.Header> <Layout.Content style={{margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280}}> <Content/> </Layout.Content> </Layout> </Layout> ) } } export default App;
修改/app/component/layout/Header.js
import React from 'react'; import avatar_img from '../../images/avatar.jpg'; import {Icon, Avatar} from 'antd'; class Header extends React.Component { constructor(props) { super(props); } render() { const { collapsed, toggle } = this.props; return ( <div className="layout-header"> <Icon className="trigger" type={collapsed ? 'menu-unfold' : 'menu-fold'} onClick={toggle} /> <Avatar className="avatar" src={avatar_img}/> </div> ) } } export default Header;
修改/app/component/layout/Sidebar.js
import React from 'react'; import {Menu, Icon} from 'antd'; class Sidebar extends React.Component { constructor(props) { super(props); } render() { return ( <div className="layout-sidebar"> <div className="logo"/> <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}> <Menu.Item key="1"> <Icon type="user"/> <span>nav 1</span> </Menu.Item> <Menu.Item key="2"> <Icon type="video-camera"/> <span>nav 2</span> </Menu.Item> <Menu.Item key="3"> <Icon type="upload"/> <span>nav 3</span> </Menu.Item> </Menu> </div> ) } } export default Sidebar;
修改/app/component/layout/Content.js
import React from 'react'; import {Table} from 'antd'; class Content extends React.Component { render() { const columns = [{ title: '姓名', dataIndex: 'name', key: 'name', }, { title: '年齡', dataIndex: 'age', key: 'age', }, { title: '電話號碼', dataIndex: 'number', key: 'number', }, { title: '郵箱', dataIndex: 'email', key: 'email', }]; const data = []; for (let i = 1; i < 15; i++) { let obj = { name: 'doudou', age: 32, number: 123456789, email: '123456789@163.com', }; obj.key = i; data.push(obj); } return ( <Table columns={columns} dataSource={data}/> ) } } export default Content;
新建layout.scss
.layout-app { height: 100%; } .layout-sidebar { .logo { height: 32px; background: #333; border-radius: 6px; margin: 16px; } } .layout-header { .trigger { font-size: 18px; line-height: 64px; padding: 0 16px; cursor: pointer; transition: color .3s; &:hover { color: #108ee9; } } .avatar { float: right; margin-top: 16px; margin-right: 16px; cursor: pointer; } }
07.引入路由react-router
安裝react-router
npm install react-router-dom --save
修改/app/app.js
import './index.html'; import './styles/app.less'; import React from 'react'; import ReactDom from 'react-dom'; import App from './components/App.jsx'; import {BrowserRouter, Route} from 'react-router-dom'; ReactDom.render( <BrowserRouter> <Route path="/" component={App}/> </BrowserRouter> , document.getElementById('root') );
修改/app/component/App.js
import React from 'react'; import Home from './Home'; import Login from './Login'; import {Route} from 'react-router-dom'; class App extends React.Component { componentDidMount() { const { location, history } = this.props; if (location.pathname === '/home' || location.pathname === '/') { history.replace('/home') } else if (location.pathname === '/login') { history.replace('/login') } } render() { return ( <div className="app"> <Route path="/home" component={Home}/> <Route path="/login" component={Login}/> </div> ) } } export default App;
添加/app/component/home/Home.js
import React from 'react'; import {matchPath} from 'react-router-dom'; import Header from './Header'; import Content from './Content'; import Sidebar from './Sidebar'; import {Layout} from 'antd'; export default class Home extends React.Component { constructor(props) { super(props); this.state = { collapsed: false }; this.ress = ['content01', 'content02', 'content03']; this.res = null; const match = matchPath(this.props.history.location.pathname, { path: '/home/:res' }); if (match) { this.res = match.params.res; } this.toggle = () => { this.setState({ collapsed: !this.state.collapsed }) }; } componentWillMount() { if (!this.res || !this.res.length || this.ress.indexOf(this.res) === -1) { this.props.history.replace(`/home/content01`) } } render() { return ( <Layout className="layout-app"> <Layout.Sider trigger={null} collapsible collapsed={this.state.collapsed} > <Sidebar res= {this.res}/> </Layout.Sider> <Layout> <Layout.Header style={{background: '#fff', padding: 0}}> <Header collapsed={this.state.collapsed} toggle={this.toggle} /> </Layout.Header> <Layout.Content style={{margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280}}> <Content/> </Layout.Content> </Layout> </Layout> ) } }
添加/app/component/login/Login.js
import React from 'react'; import {Form, Icon, Input, Button, Checkbox} from 'antd'; const FormItem = Form.Item; class Logining extends React.Component { constructor(props) { super(props); this.handleSubmit = (e) => { const { history } = this.props; e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { if (values.userName === 'admin' && values.password === '123456') { history.replace('/home') } console.log('Received values of form: ', values); } }); } } render() { const { getFieldDecorator } = this.props.form; return ( <div className="login"> <Form onSubmit={this.handleSubmit} className="login-form"> <FormItem> {getFieldDecorator('userName', { rules: [{required: true, message: 'Please input your username!'}], })( <Input prefix={<Icon type="user" style={{fontSize: 13}}/>} placeholder="Username"/> )} </FormItem> <FormItem> {getFieldDecorator('password', { rules: [{required: true, message: 'Please input your Password!'}], })( <Input prefix={<Icon type="lock" style={{fontSize: 13}}/>} type="password" placeholder="Password"/> )} </FormItem> <FormItem> {getFieldDecorator('remember', { valuePropName: 'checked', initialValue: true, })( <Checkbox>Remember me</Checkbox> )} <a className="login-form-forgot" href="">Forgot password</a> <Button type="primary" htmlType="submit" className="login-form-button"> Log in </Button> Or <a href="">register now!</a> </FormItem> </Form> </div> ); } } const Login = Form.create()(Logining); export default Login;
修改/app/component/layout/Sidebar.js
import React from 'react'; import {Menu, Icon} from 'antd'; import {NavLink} from 'react-router-dom'; export default class Sidebar extends React.Component { render() { const { res } = this.props; return ( <div className="layout-sidebar"> <div className="logo"/> <Menu theme="dark" mode="inline" defaultSelectedKeys={[res]} > <Menu.Item key="content01"> <NavLink to="/home/content01"> <Icon type="user"/> <span>nav 1</span> </NavLink> </Menu.Item> <Menu.Item key="content02"> <NavLink to="/home/content02"> <Icon type="video-camera"/> <span>nav 2</span> </NavLink> </Menu.Item> <Menu.Item key="content03"> <NavLink to="/home/content03"> <Icon type="upload"/> <span>nav 3</span> </NavLink> </Menu.Item> </Menu> </div> ) } }
修改/app/component/layout/Content.js
import React from 'react'; import {Route} from 'react-router-dom'; import Content01 from './content01'; import Content02 from './content02'; import Content03 from './content03'; export default class Content extends React.Component { render() { return ( <div> <Route path="/home/Content01" component={Content01}/> <Route path="/home/Content02" component={Content02}/> <Route path="/home/Content03" component={Content03}/> </div> ) } }
修改/app/index.html
<!DOCTYPE html> <html lang="en"> <head> <base href="/"> <meta charset="UTF-8"> <title>hello world</title> </head> <body> <div id="root"></div> <script src="./app.js"></script> </body> </html>
08.引入狀態管理react-redux
安裝redux
npm i redux react-redux --save
新建/app/store/store.js
import {createStore} from 'redux'; import user from '../components/content01.reducer'; const store = createStore(user); export default store;
修改/app/app.js
import {Provider} from 'react-redux'; import store from './store/store'; ReactDom.render( <Provider store={store}> <BrowserRouter> <Route path="/" component={App}/> </BrowserRouter> </Provider> , document.getElementById('root') );
新建/app/component/content01.reducer.js
let initState = { data: [{ name: 'doudou', age: 32, phone: 123456789, email: '123456789@163.com', key: 1, }] }; function user(state = initState, action) { const data = state.data; switch (action.type) { case 'ADD_ONE_USER': data[data.length] = action.payload; return {data: data}; default: return state; } } export default user;
新建/app/component/content01.action.js
export function addOneUser(value) { return { type: 'ADD_ONE_USER', payload: value } }
修改/app/component/content01.jsx
import React from 'react'; import {Table, Icon, Button} from 'antd'; import {connect} from 'react-redux'; import {addOneUser} from './content01.action'; import FromContent from './FromContent'; class Content01 extends React.Component { constructor(props) { super(props); this.state = { visible: false, date: null }; this.showModal = () => { this.setState({ visible: true, data: new Date() }); }; this.closeModal = () => { this.setState({ visible: false, }); }; this.submit = (values) => { values.key = Date.parse(new Date()); this.props.addOneUser(values); this.closeModal(); } } render() { const columns = [{ title: '姓名', dataIndex: 'name', key: 'name', }, { title: '年齡', dataIndex: 'age', key: 'age', }, { title: '電話號碼', dataIndex: 'phone', key: 'phone', }, { title: '郵箱', dataIndex: 'email', key: 'email', }]; const data = this.props.data; return ( <div> <Button type="primary" onClick={this.showModal}> <Icon type="user-add"/>添加 </Button> <Table columns={columns} dataSource={data}/> { this.state.visible ? <FromContent date={this.state.date} submit={this.submit} showModal={this.showModal} closeModal={this.closeModal} /> : null } </div> ) } } const mapStateToProps = (state) => { return { data: state.data } }; const mapDispatchToProps = (dispatch) => { return { addOneUser: (value) => dispatch(addOneUser(value)) } }; export default connect(mapStateToProps, mapDispatchToProps)(Content01);
新建/app/component/FromContent.js
import React from 'react'; import {Modal, Button, Form, Input} from 'antd'; const FormItem = Form.Item; import {increaseAction} from './content01.action'; class FromContent01 extends React.Component { constructor(props) { super(props); this.handleSubmit = (e) => { e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { console.log('Received values of form: ', values); this.props.submit(values); } }); } } render() { const {getFieldDecorator} = this.props.form; const {closeModal, date} = this.props; return ( <Modal title="添加一個成員" key={date} visible={true} onCancel={closeModal} footer={null} > <Form onSubmit={this.handleSubmit} className="login-form"> <FormItem label="姓名"> {getFieldDecorator('name', { rules: [{required: true, message: 'Please input your name!'}], })( <Input placeholder="姓名"/> )} </FormItem> <FormItem label="年齡"> {getFieldDecorator('age', { rules: [{required: true, message: 'Please input your age!'}], })( <Input placeholder="年齡"/> )} </FormItem> <FormItem label="電話號碼"> {getFieldDecorator('phone', { rules: [{ required: true, message: 'Please input your phone!', }], })( <Input type="電話號碼"/> )} </FormItem> <FormItem label="郵箱"> {getFieldDecorator('email', { rules: [{ required: true, message: 'Please input your email!', }], })( <Input type="郵箱"/> )} </FormItem> <FormItem> <Button type="primary" htmlType="submit" className="login-form-button"> Log in </Button> </FormItem> </Form> </Modal> ) } } const FromContent = Form.create()(FromContent01); export default FromContent;
09.更好的使用redux
redux-freeze運行時報錯機制
redux-thunkredux的異步中間件
redux-logger日誌中間件
redux-immutable在不可變數據類型中合併reducer
redux-create-reducer使用action和reducer的關聯變的簡單
安裝依賴插件
npm i --save redux-immutable redux-freeze redux-thunk redux-logger immutable redux-create-reducer
修改/app/store/store.js
import {createStore, applyMiddleware, compose} from 'redux'; import {combineReducers} from 'redux-immutable'; import freeze from "redux-freeze" import thunk from 'redux-thunk' import logger from 'redux-logger' import {Map} from 'immutable'; import reducers from './reducers'; let middlewares = []; middlewares.push(thunk); middlewares.push(logger); middlewares.push(freeze); //添加中間件 let middleware = applyMiddleware(...middlewares); //添加redux dev tools,能夠在谷歌商城裏直接安裝工具,搜索名字 middleware = compose(middleware, window.devToolsExtension()); const reducer = combineReducers(reducers); const store = createStore( reducer, Map({}), middleware ); export default store;
新建/app/store/reducers.js
import user from '../components/content01.reducer'; export default { user }
修改/app/component/content01.reducer.js
import {createReducer} from 'redux-create-reducer'; import {fromJS} from 'immutable'; import { ADD_ONE_USER } from './content01.actions'; const initState = fromJS({ data: [{ name: 'doudou', age: 32, phone: 123456789, email: '123456789@163.com', key: 1, }] }); const handlers = { [ADD_ONE_USER]: (user, action) => { return user.set('data', user.get('data').push(fromJS(action.payload))); } }; export default createReducer(initState, handlers);
修改/app/component/content01.action.js
export const ADD_ONE_USER = 'ADD_ONE_USER'; export function addOneUser(value) { return dispatch => { return dispatch({ type: 'ADD_ONE_USER', payload: value }) } }
修改/app/component/content01.js
const mapStateToProps = (state) => { return { data: state.get('user').get('data'), } }; const mapActionCreators = { addOneUser }; export default connect(mapStateToProps, mapActionCreators)(Content01);
10.優化環境
下載須要的依賴
npm i extract-text-webpack-plugin copy-webpack-plugin clean-webpack-plugin webpack-merge html-webpack-plugin postcss-smart-import postcss-loader precss --save-dev
將webpack.config.js拆分
webpack.common.js
const path = require('path'); const ROOTPATH = path.resolve(__dirname, '.'); module.exports = { context: path.resolve(ROOTPATH, 'app'), resolve: { extensions: ['.js', '.jsx'], //後綴名自動補全,可使用jsx的文件後綴 modules: [path.resolve(ROOTPATH, "node_modules")], alias: { COMPONENTS_PATH: './components', } }, entry: { app: ['babel-polyfill', './app.js'] }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['env', 'react'], } } }, { test: /.*\.(gif|png|jpe?g|svg)$/, use: [{ loader: 'url-loader', options: { limit: 10000, name: 'images/[name].[ext]' } }, { loader: 'image-webpack-loader' }] }, { test: /\.html$/, use: [{ loader: 'html-loader', options: { minimize: false } }], }, { test: /\.(woff|svg|eot|ttf)\??.*$/, use: [{ loader: 'url-loader', options: { limit: 50000, } }] } ] }, devServer: { contentBase: path.resolve(ROOTPATH, './app'),//爲一個目錄下的文件提供本地服務器,在這裏設置其所在目錄 historyApiFallback: true,//跳轉將指向index.html inline: true,//開啓自動刷新頁面 port: 1234,//設置默認監聽端口,若是省略,默認爲"8080" hot: true,//開啓熱替換 }, plugins: [], };
webpack.dev.js
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); const webpack = require('webpack'); const path = require('path'); const ROOTPATH = path.resolve(__dirname, '.'); module.exports = merge(common, { output: { path: path.resolve(ROOTPATH, 'dist'), //打包的文件夾 filename: '[name].js', publicPath: '' }, module: { rules: [ { test: /\.css$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], }, { test: /\.scss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true, outputStyle: 'expanded', sourceMapContents: true } }] }, { test: /\.less$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'less-loader', options: { sourceMap: true, outputStyle: 'expanded', sourceMapContents: true, } }] }, { test: /index\.html/, use: [{ loader: 'file-loader', options: { name: 'index.[ext]' } }] } ] }, plugins: [], });
webpack.pro.js
const path = require('path'); const merge = require('webpack-merge'); const common = require('./webpack.common.js'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; const HtmlWebpackPlugin = require('html-webpack-plugin'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; const ROOTPATH = path.resolve(__dirname, '.'); module.exports = merge(common, { output: { path: path.resolve(ROOTPATH, 'dist'), //打包的文件夾 filename: '[name].[hash].bundle.js', publicPath: '' }, module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({fallback: 'style-loader', use: ['css-loader']}), }, { test: /\.scss$/, use: ExtractTextPlugin.extract({ use: [{ loader: 'css-loader' }, { loader: 'sass-loader' }] }) }, { test: /\.less$/, use: ExtractTextPlugin.extract({ use: [{ loader: "css-loader", options: {importLoaders: 1} }, { loader: "postcss-loader" }, { loader: 'less-loader' }] }) }, ] }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }), new ExtractTextPlugin({ filename: '[name].[contenthash].css', allChunks: true, // don't contain embedded styles }), //加入js壓縮的實例 new UglifyJsPlugin({ mangle: { mangle: false }, compress: { sequences: true, dead_code: true, conditionals: true, booleans: true, unused: false, if_return: true, join_vars: true, drop_console: false, warnings: false }, }), new HtmlWebpackPlugin({ title: 'xxx局', filename: path.resolve(ROOTPATH, 'dist/template.html'), // the path to create html template: path.resolve(ROOTPATH, 'app/public/template.html'), //the path of template html, minify: false, // favicon: path.resolve(__dirname, 'app/images/cscec_favicon-2.ico') }), new CleanWebpackPlugin(['dist', 'dist.zip', 'dist.rar'], {exclude: ['lib']}), new CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js', minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, './node_modules') ) === 0 ) } }), ], });
建立app/public/template.html
修改package.json
"scripts": { "build": "set NODE_ENV=production&&webpack -p --config webpack.pro.js", "start": "webpack-dev-server --devtool eval --progress --colors --config webpack.dev.js " },
建立postcss.config.js
module.exports = { plugins: [ require('postcss-smart-import')({ /* ...options */ }), require('precss')({ /* ...options */ }), require('autoprefixer')({ /* ...options */ }) ] };
執行npm run build
生成的代碼已經進過了分離,壓縮,添加了版本號,可以利用緩存提高加載速度
11.在react中使用jsplumb、jquery和jquery-ui。
方法1.ProvidePlugin
自動加載模塊,不用再每一個組件使用require和import
<!--在webpack.common.js的plugins中添加--> new webpack.ProvidePlugin({ "$": "jquery", "jQuery": "jquery", "window.jQuery": "jquery" })
此時可使用全局變量$和jQuery了,能夠在任意一個組件中console.log($)
就能夠看到一個函數
<!--引入須要jquery的插件--> import jp from '../../plugins/jsplumb/jsplumb'; import "jquery-ui/ui/widgets/draggable";
接下來就可使用jquery和jquery的插件了
<!--可使用$以及使用jquery-ui的draggable方法了--> $(el).draggable({ helper: "clone", containment: ".js-layout",//設置拖動的範圍 scroll: false, cursor: "pointer",//設置拖動光標老是在中心 cursorAt: {top: 30, left: 60}, start: function (ev) { wh = { w: $(this).width() / 2, h: $(this).height() / 2 } }, drag: function (ev) { offset = $(this).offset(); }, stop: function (ev) { console.log('鼠標的位置', {top: ev.clientY, left: ev.clientX}); console.log('拖放元素的位置', {top: offset.top, left: offset.left}); let pos = {x: ev.clientX - offset.left - wh.w - 9, y: ev.clientY - offset.top - wh.h - 100 - 8}; movecb(pos) } });
方法2.expose-loader
使用expose-loader能夠暴露全局變量
module: { rules: [ { test: require.resolve('jquery'), use: [{ loader: 'expose-loader', options: 'jQuery' }, { loader: 'expose-loader', options: '$' }] }, ] },
此時的效果和方法一的同樣 webpack文檔
使用jquery-ui的方法
使用npm
<!--能夠經過npm i下載--> npm i jquery-ui --save <!--而後在組件中引入--> import "jquery-ui/ui/widgets/draggable";
須要一個個引入你須要的方法,也能夠作一個總文件進行引入
直接下載
從官網下載jquery-ui,而後放到項目中的一個文件夾下
import "../../plugins/jquery-ui"
這樣能夠直接引入生效,可是打包的文件會大很多
查看文章
webpack多頁應用架構系列(四):老式jQuery插件還不能丟,怎麼兼容? Webpack引入jquery及其插件的幾種方法
12.開發經常使用工具
- prop-types
- classnames
附1.依賴
搭建框架
babel基本
-
[babel-cli]
-
babel-core 能夠對一些代碼直接調用Babel的API進行轉碼
-
babel-loader 使babel在webpack中支持
-
babel-preset-env 最新的標準版本取代stage0,1,2,3等等
-
babel-preset-react 預設包含了全部的 React 插件
babel其餘
-
babel-plugin-react-transform 任意轉換的方式去封裝 React 組件
-
babel-plugin-transform-object-rest-spread 使對象支持Object rest spread,展開運算符
-
babel-plugin-transform-remove-strict-mode 將"use strict"去掉,看需求可用可不用 官網
-
babel-plugin-transform-runtime 在 Babel 轉換過程當中,詳細的展現引用的相關輔助工具和內置命令,並自動的聚合填充你的代碼而不會污染全局
react
react-router
[react-router-dom]
redux
redux-freeze運行時報錯機制
redux-thunkredux的異步中間件
redux-logger日誌中間件
redux-immutable在不可變數據類型中合併reducer
redux-create-reducer使用action和reducer的關聯變的簡單
ant-design
babel-plugin-import 用於antd按需加載
loader
[file-loader]
[url-loader]
[image-webpack-loader]
[extract-text-webpack-plugin] 將css單獨打包