不知道该写啥QAQ 2020-06-10
本文详细介绍了如何从零开始搭建一个 React 开发的脚手架,包含如何添加 Redux 以及 React Router 的环境。
本文代码地址:react-mobx-starter。
建议将代码拉下来之后,配合本文一起查看,效果更佳。
代码下载命令:
git clone -b example https://github.com/beichensky/react-mobx-starter.git 复制代码
最近将脚手架中的 babel 配置更新到了 7.0.0 版本,所以部分地方作出了修改。目前脚手架中的各类库的版本如下:
如果是从低版本升级到 7.0.0
版本,官方提供了一个新的命令可以直接帮助我们对项目进行更新:
npx babel-upgrade --write --install 复制代码
更多关于 babel-upgrade
的介绍可以参考官方说明 。
本文的 Demo
分为两个环境,一个是开发环境,另一个是生产环境。
开发环境中讲述的是如何配置出一个更好的、更方便的开发环境;
而生产环境中讲述的是如何配置出一个更优化、更小版本的生产环境。
之前我也就 Webpack
的使用写了几篇文章,本文也是在 Webpack
的基础上进行开发,也是在之前的代码上进行的扩展。
开发环境的配置是在 从零开始搭建一个 Webpack 开发环境配置(附 Demo) 一文的基础上进行的扩展。
生产环境的配置是在 使用 Webpack 进行生产环境配置(附 Demo) 一文的基础上进行的扩展。
建议:对于 Webpack 还不了解的朋友,可以先看一下 从零开始搭建一个 Webpack 开发环境配置(附 Demo) 和 使用 Webpack 进行生产环境配置(附 Demo) 这两篇文章,可以更好的入手本文。
虽然本文是在之前文章上进行的扩展,但本文还是会详细的介绍每一步的配置。
新建文件夹,命名为:react-mobx-starter
mkdir react-mobx-starter 复制代码
初始化 package.json
文件
cd react-mobx-starter # 直接生成默认的 package.json 文件 npm init -y 复制代码
创建 src
目录,用来存放我们编写的代码 创建 public
目录,用来存放公共的文件 创建 Webpack
目录,用来存放 Webpack
配置文件
mkdir src mkdir public mkdir webpack 复制代码
在 src 目录下 新建 pages 文件夹,用来存放书写的页面组件 新建 components 文件夹,用来存放公共组件 新建 utils 文件夹,用来存放常用的工具类
cd src mkdir pages mkdir components mkdir utils 复制代码
在 public
目录下新建 index.html
文件 在 src
目录下新建 index.js
文件 在 Webpack
目录下创建 webpack.config.dev.js
和 webpack.config.prod.js
webpack.config.dev.js
用来编写 webpack 开发环境配置webpack.config.prod.js
用来编写 webpack 生产环境配置index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>React + Mobx 全家桶脚手架</title> </head> <body> <div id="root"></div> </body> </html> 复制代码
index.js
function createElement() { const ele = document.createElement(‘div‘); ele.innerHTML = ‘Hello, React‘; const root = document.querySelector(‘#root‘); root.appendChild(ele); } createElement(); 复制代码
webpack.config.dev.js
和 webpack.config.prod.js
此时还没有书写内容,我们之后会详细的进行讲述。
我们看一下此时的项目结构,之后就可以进行 webpack 的配置了。
react-mobx-starter ├─ public/ └─ index.html ├─ src/ ├─ components/ ├─ pages/ ├─ utils/ └─ index.js ├─ webpack/ ├─ webpack.config.dev.js └─webpack.config.prod.js ├─ package.json 复制代码
在 package.json 文件中添加一个执行脚本,用来执行 webpack 命令:
{ ..., "scripts": { "start": "webpack --config webpack/webpack.config.dev.js" }, ... } 复制代码
安装 webpack 和 webpack-cli
npm install webpack webpack-cli --save-dev 复制代码
使用 webpack 进行项目配置的时候,必须要有入口和出口,作为模块引入和项目输出。
webpack.config.dev.js
const path = require(‘path‘); const appSrc = path.resolve(__dirname, ‘../src‘); const appDist = path.resolve(__dirname, ‘../dist‘); const appPublic = path.resolve(__dirname, ‘../public‘); const appIndex = path.resolve(appSrc, ‘index.js‘); module.exports = { entry: appIndex, output: { filename: ‘public/js/[name].[hash:8].js‘, path: appDist, publicPath: ‘/‘ } } 复制代码
执行 npm run start 脚本,可以看到 dist/public/js
目录下多了一个 js
文件,但是这个是由 hash
值命名的的,我们每次都手动引入到 index.html 文件里面过于麻烦,所以可以引入 html-webpack-plugin
插件。
html-webpack-plugin 插件有两个作用
public
目录下的文件夹拷贝到 dist
输出文件夹下dist
下的 js
文件引入到 html
文件中html-webpack-plugin
插件npm install html-webpack-plugin --save-dev 复制代码
html-webpack-plugin
插件webpack.config.dev.js
const path = require(‘path‘); + const HTMLWebpackPlugin = require(‘html-webpack-plugin‘); const appSrc = path.resolve(__dirname, ‘../src‘); const appDist = path.resolve(__dirname, ‘../dist‘); const appPublic = path.resolve(__dirname, ‘../public‘); const appIndex = path.resolve(appSrc, ‘index.js‘); + const appHtml = path.resolve(appPublic, ‘index.html‘); module.exports = { entry: appIndex, output: { filename: ‘public/js/[name].[hash:8].js‘, path: appDist, publicPath: ‘/‘ }, + plugins: [ + new HTMLWebpackPlugin({ + template: appHtml, + filename: ‘index.html‘ + }) + ] } 复制代码
webpack 配置中的 mode 属性,可以设置为 ‘development‘ 和 ‘production‘,我们目前是进行开发环境配置,所以可以设置为 ‘development‘
webpack.config.dev.js
... module.exports = { + mode: ‘development‘, ... } 复制代码
为了方便在项目出错时,迅速定位到错误位置,可以设置 devtool,生成资源映射,我们这里使用 inline-source-map
,更多选择可以在这里查看区别。
webpack.config.dev.js
... module.exports = { mode: ‘development‘, + devtool: ‘inline-source-map‘, ... } 复制代码
npm install webpack-dev-server --save-dev 复制代码
webpack.config.dev.js
... module.exports = { mode: ‘development‘, devtool: ‘inline-source-map‘, + devServer: { + contentBase: appPublic, + hot: true, + host: ‘localhost‘, + port: 8000, + historyApiFallback: true, + // 是否将错误展示在浏览器蒙层 + overlay: true, + inline: true, + // 打印信息 + stats: ‘errors-only‘, + // 设置代理 + proxy: { + ‘/api‘: { + changeOrigin: true, + target: ‘https://easy-mock.com/mock/5c2dc9665cfaa5209116fa40/example‘, + pathRewrite: { + ‘^/api/‘: ‘/‘ + } + } + } + }, ... } 复制代码
修改一下 package.json
文件中的 start
脚本:
{ ..., "scripts": { "start": "webpack-dev-server --config webpack/webpack.config.dev.js" }, ... } 复制代码
friendly-errors-webpack-plugin
插件可以在命令行展示更有好的提示功能。
安装 friendly-errors-webpack-plugin
:
npm install friendly-errors-webpack-plugin --save-dev 复制代码
使用 friendly-errors-webpack-plugin
:
webpack.config.dev.js
const path = require(‘path‘); const HTMLWebpackPlugin = require(‘html-webpack-plugin‘); + const FriendlyErrorsWebpackPlugin = require(‘friendly-errors-webpack-plugin‘); ... module.exports = { ... plugins: [ new HTMLWebpackPlugin({ template: appHtml, filename: ‘index.html‘ }), + new FriendlyErrorsWebpackPlugin(), ] } 复制代码
webpack.config.dev.js
const path = require(‘path‘); const HTMLWebpackPlugin = require(‘html-webpack-plugin‘); const FriendlyErrorsWebpackPlugin = require(‘friendly-errors-webpack-plugin‘); + const webpack = require(‘webpack‘); ... module.exports = { ... plugins: [ ... new FriendlyErrorsWebpackPlugin(), + new webpack.HotModuleReplacementPlugin() ] } 复制代码
执行 npm run start
命令,命令行提示成功后,在浏览器打开 http://localhost:8000
,可以看到 Hello React
,说明基本的Webpack 配置已经成功了。
我们现在 index.js
里面的代码量比较少,所以没有问题。但是我如果想在里面使用一些 ES6 的语法或者是还未被标准定义的 JS 特性,那么我们就需要使用 babel 来进行转换了。下面我们来配置一下 babel
。
npm install @babel/core babel-loader --save-dev 复制代码
设置 cacheDirectory 属性,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。
webpack.config.dev.js
... module.exports = { ... plugins: [ ... ], module: { rules: [ { test: /\.(js|jsx)$/, loader: ‘babel-loader?cacheDirectory‘, include: [ appSrc ], exclude: /node_modules/ } ] } } 复制代码
babel.config.js
文件是运行时控制文件,在项目编译的时候会自动读取 babel.config.js
文件中的 babel
配置。
安装相关插件:
@babel/preset-env
:可以在项目中使用所有 ECMAScript
标准里的最新特性。@babel/preset-react
:可以在项目中使用 react
语法。npm install babel-preset-env babel-preset-react --save-dev 复制代码
配置 babel.config.js
文件:
module.exports = (api) => { api.cache(true); return { presets: [ "@babel/preset-env", "@babel/preset-react" ] } } 复制代码
babel
升级到 7.0.0
版本之后, @babel/preset-stage-0
被废弃,用到的插件需要自己进行安装。 如果是从低版本升级到 7.0.0
版本,官方提供了一个新的命令可以直接帮助我们对项目进行更新:
npx babel-upgrade --write --install 复制代码
更多关于 babel-upgrade
的介绍可以参考官方说明 。
安装相关插件:
@babel/plugin-proposal-decorators
:可以在项目中使用装饰器语法。@babel/plugin-proposal-class-properties
:可以在项目中使用新的 class 属性语法。@babel/plugin-transform-runtime
:使用此插件可以直接使用 babel-runtime 中的代码对 js
文件进行转换,避免代码冗余。@babel/runtime-corejs2
:配合 babel-plugin-transform-runtime
插件成对使用@babel/plugin-syntax-dynamic-import
:可以在项目中使用 import()
这种语法@babel/plugin-proposal-export-namespace-from
:可以使用 export * 这种命名空间的方式导出模块@babel/plugin-proposal-throw-expressions
:可以使用异常抛出表达式@babel/plugin-proposal-logical-assignment-operators
:可以使用逻辑赋值运算符@babel/plugin-proposal-optional-chaining
:可以使用可选链的方式访问深层嵌套的属性或者函数 ?.@babel/plugin-proposal-pipeline-operator
:可以使用管道运算符 |>@babel/plugin-proposal-nullish-coalescing-operator
:可以使用空值合并语法 ??@babel/plugin-proposal-do-expressions
:可以使用 do 表达式(可以认为是三元运算符的复杂版本)@babel/plugin-proposal-function-bind
:可以使用功能绑定语法 obj::funcnpm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime @babel/runtime-corejs2 @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-export-namespace-from @babel/plugin-proposal-throw-expressions @babel/plugin-proposal-logical-assignment-operators @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-pipeline-operator @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-do-expressions @babel/plugin-proposal-function-bind --save-dev 复制代码
配置 babel.config.js
文件:
module.exports = (api) => { api.cache(true); return { presets: [ "@babel/preset-env", "@babel/preset-react" ], plugins: [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], [ "@babel/plugin-transform-runtime", { "corejs": 2 } ], [ "@babel/plugin-proposal-class-properties", { "loose": true } ], "@babel/plugin-syntax-dynamic-import", // 可以使用 export * 这种命名空间的方式导出模块 "@babel/plugin-proposal-export-namespace-from", // 可以使用异常抛出表达式, "@babel/plugin-proposal-throw-expressions", // 默认导出 "@babel/plugin-proposal-export-default-from", // 可以使用逻辑赋值运算符 "@babel/plugin-proposal-logical-assignment-operators", // 可以使用可选链的方式访问深层嵌套的属性或者函数 ?. "@babel/plugin-proposal-optional-chaining", // 可以使用管道运算符 |> [ "@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" } ], // 可以使用空值合并语法 ?? "@babel/plugin-proposal-nullish-coalescing-operator", // 可以使用 do 表达式(可以认为是三元运算符的复杂版本) "@babel/plugin-proposal-do-expressions", // 可以使用功能绑定语法 obj::func "@babel/plugin-proposal-function-bind" ] } } 复制代码
这里需要注意
@babel/plugin-proposal-decorators
插件的放置顺序,最好放在第一位,否则可能会出现某些注解失效的问题。
至此,babel 相关的基本配置完成了。之后我们就可以在项目中肆意使用各种新的 JS 特性了。
js
文件相关的 babel-loader
配置好了,但是有时候我们想在项目中为元素添加一些样式,而 Webpack
中认为一切都是模块,所以我们这时候也需要别的 loader
来解析一波样式代码了。
css-loader
:处理 css
文件中的 url()
等。style-loader
:将 css
插入到页面的 style
标签。less-loader
:是将 less
文件编译成 css
。postcss-loader
:可以集成很多插件,用来操作 css
。我们这里使用它集成 autoprefixer
来自动添加前缀。npm install css-loader style-loader less less-loader postcss-loader autoprefixer --save-dev 复制代码
react
无法直接使用类似 Vue
中 scope
这种局部作用变量,所以我们可以使用 Webpack
提供的 CSS Module
。 2、由于等会儿会使用 antd
,所以引入 antd
时需要开启 less
的 javascript
选项,所以要将 less-loader
中的属性 javascriptEnabled
设置为 true
。在 webpack.config.dev.js 中配置:
... const autoprefixer = require(‘autoprefixer‘); module.exports = { ..., plugins: [...], module: { rules: [ ..., { test: /\.(css|less)$/, exclude: /node_modules/, use: [{ loader: ‘style-loader‘ }, { loader: ‘css-loader‘, options: { sourceMap: true, modules: true, localIdentName: ‘[local].[hash:8]‘ } }, { loader: ‘postcss-loader‘, options: { plugins: () => [autoprefixer()] } }, { loader: ‘less-loader‘, options: { javascriptEnabled: true } } ] }, { test: /\.(css|less)$/, include: /node_modules/, use: [{ loader: ‘style-loader‘ }, { loader: ‘css-loader‘, options: {} }, { loader: ‘postcss-loader‘, options: { plugins: () => [autoprefixer()] } }, { loader: ‘less-loader‘, options: { javascriptEnabled: true } } ] }, ] } } 复制代码
安装相关插件:
npm install file-loader csv-loader xml-loader html-loader markdown-loader --save-dev 复制代码
在 webpack.config.dev.js 中配置:
... module.exports = { ..., plugins: [...], module: { rules: [ ..., // 解析图片资源 { test: /\.(png|svg|jpg|gif)$/, use: [ ‘file-loader‘ ] }, // 解析 字体 { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ ‘file-loader‘ ] }, // 解析数据资源 { test: /\.(csv|tsv)$/, use: [ ‘csv-loader‘ ] }, // 解析数据资源 { test: /\.xml$/, use: [ ‘xml-loader‘ ] }, // 解析 MakeDown 文件 { test: /\.md$/, use: [ ‘html-loader‘, ‘markdown-loader‘ ] } ] } } 复制代码
在项目开发过程中,随着项目越来越大, 文件层级越来越深,引入文件的时候可能会需要一层一层的找路径,就会比较繁琐,我们可以使用 resolve
中的 alias
属性为一些常用的文件夹设置别名
webpack.config.dev.js
··· module.exports = { ..., plugins: [...], module: {...}, + resolve: { + alias: { + src: appSrc, + utils: path.resolve(__dirname, ‘../src/utils‘), + pages: path.resolve(__dirname, ‘../src/pages‘), + components: path.resolve(__dirname, ‘../src/components‘) + } + } } 复制代码
我们知道,一般进行模块搜索时,会从当前目录下的 node_modules
一直搜索到磁盘根目录下的 node_modules
。所以为了减少搜索步骤,我们可以设置 resolve.modules
属性强制只从项目的 node_modules
中查找模块。
webpack.config.dev.js
··· module.exports = { ..., plugins: [...], module: {...}, resolve: { ..., + modules: [path.resolve(__dirname, ‘../node_modules‘)], } } 复制代码
npm install react react-dom prop-types mobx mobx-react react-router-dom --save 复制代码
按照 antd
官网的说明,直接在 babel.config.js
文件中添加配置,之后即可在项目中正常使用了。
安装 antd 相关插件:
npm install antd moment --save 复制代码
安装 babel-plugin-import 对组件进行按需加载:
npm install babel-plugin-import --save-dev 复制代码
在 babel.config.js
文件中添加 antd 配置:
module.exports = (api) => { api.cache(true); return { presets: [ ... ], plugins: [ ..., + [ + "import", + { + "libraryName": "antd", + "style": true + } + ], ... ] } 复制代码
基本上需要的插件目前都已经引入了,是时候进行开发了。
index.js
import React from ‘react‘; import ReactDom from ‘react-dom‘; import { Provider } from ‘mobx-react‘ import { LocaleProvider } from ‘antd‘; import { HashRouter } from ‘react-router-dom‘; import zh_CN from ‘antd/lib/locale-provider/zh_CN‘; import ‘moment/locale/zh-cn‘; import GlobalModel from ‘./GlobalModel‘; // import App from ‘./App‘; const globalModel = new GlobalModel(); const App = () => { return <div>开发环境配置完成</div> } ReactDom.render( <Provider globalModel={ globalModel }> <LocaleProvider locale={zh_CN}> <HashRouter> <App /> </HashRouter> </LocaleProvider> </Provider>, document.querySelector(‘#root‘) ); 复制代码
运行 npm run start
命令,在浏览器打开 http://localhost:8000/
,就能够看到 开发环境配置完成 正常显示。
此时说明我们各种插件、库都已经引入完成,可以正常使用了。
App.js
import React from ‘react‘; import { Switch, Route } from ‘react-router-dom‘; import Home from ‘pages/home‘; import Settings from ‘pages/settings‘; import Display from ‘pages/display‘; import NotFound from ‘pages/exception‘ import styles from ‘./App.less‘; export default (props) => { return ( <div className={ styles.app }> <Switch> <Route path=‘/settings‘ component={ Settings } /> <Route path=‘/display‘ component={ Display } /> <Route exact path=‘/‘ component={ Home } /> <Route component={ NotFound } /> </Switch> </div> ) } 复制代码
在 src
目录下创建 App.less
文件,编写 App
组件样式
App.less
.app { padding: 60px; } 复制代码
Home
组件是根路由组件,用来跳转到 Setting
界面和 Display
界面Settings
组件演示了如何获取和修改 mobx
的全局 Model
Display
组件演示了如何使用 mobx 进行同步和异步的数据处理NotFound
组件在匹配不到正确路由时展示Home、Settings、Display 相关的代码我就不贴了,占的篇幅较长,大家需要的话可以去我的 Github 上看一下或者下载下来也可以。比较方便。地址:Github
index.js
import React from ‘react‘; import ReactDom from ‘react-dom‘; import { Provider } from ‘mobx-react‘ import { LocaleProvider } from ‘antd‘; import { HashRouter } from ‘react-router-dom‘; import zh_CN from ‘antd/lib/locale-provider/zh_CN‘; import ‘moment/locale/zh-cn‘; import GlobalModel from ‘./GlobalModel‘; import App from ‘./App‘; const globalModel = new GlobalModel(); ReactDom.render( <Provider globalModel={ globalModel }> <LocaleProvider locale={zh_CN}> <HashRouter> <App /> </HashRouter> </LocaleProvider> </Provider>, document.querySelector(‘#root‘) ); 复制代码
可以看到这里有一个 GlobalModel
存放全局通用数据的 Model,里面的逻辑比较简单,我们稍微看一下。
GlobalModel.js
import { observable, action } from ‘mobx‘; export default class GlobalModel { @observable username = ‘小明‘; @action changeUserName = (name) => { this.username = name; } } 复制代码
由于我们在 Display 组件中需要进行网络请求的异步操作,所以我们这里引入 fetch 进行网络请求。
npm install whatwg-fetch qs --save 复制代码
在 utils
目录下创建 request.js
文件。
utils/request.js
import ‘whatwg-fetch‘; import { stringify } from ‘qs‘; /** * 使用 Get 方式进行网络请求 * @param {*} url * @param {*} data */ export const get = (url, data) => { const newUrl = url + ‘?‘ + stringify(data) + (stringify(data) === ‘‘ ? ‘‘ : ‘&‘) +‘_random=‘ + Date.now(); return fetch(newUrl, { cache: ‘no-cache‘, headers: { ‘Accept‘: ‘application/json‘, ‘Content-Type‘: ‘application/json; charset=utf-8‘ }, method: ‘GET‘, }) .then(response => response.json()); } /** * 进行 Post 方式进行网络请求 * @param {*} url * @param {*} data */ export const post = (url, data) => { return fetch(url, { body: JSON.stringify(data), cache: ‘no-cache‘, headers: { ‘Accept‘: ‘application/json‘, ‘Content-Type‘: ‘application/json; charset=utf-8‘ }, method: ‘POST‘, }) .then(response => response.json()) // parses response to JSON } 复制代码
执行 npm run start
,编译成功后,可以看到界面长这样。
Home 界面:
Settings 界面:
Display 界面:
NotFound 界面:
{ ..., "scripts": { "start": "webpack-dev-server --config webpack/webpack.config.dev.js", "build": "webpack --config webpack/webpack.config.prod.js" }, ... } 复制代码
其中大部分的 module
和 plugin
还有 resolve
都与开发环境的一致。所以我们就以 webpack.config.dev.js
文件中的配置为基础进行说明。
安装相关插件:
npm install uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin --save-dev 复制代码
添加代码压缩配置:
webpack.config.prod.js
...; const UglifyJsPlugin = require(‘uglifyjs-webpack-plugin‘); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); ...; module.exports = { mode: ‘production‘, devtool: ‘hidden-source-map‘, entry: ..., output: {...}, plugins: [...], module: {...}, optimization: { // 打包压缩js/css文件 minimizer: [ new UglifyJsPlugin({ uglifyOptions: { compress: { // 在UglifyJs删除没有用到的代码时不输出警告 warnings: false, // 删除所有的 `console` 语句,可以兼容ie浏览器 drop_console: true, // 内嵌定义了但是只用到一次的变量 collapse_vars: true, // 提取出出现多次但是没有定义成变量去引用的静态值 reduce_vars: true, }, output: { // 最紧凑的输出 beautify: false, // 删除所有的注释 comments: false, } } }), new OptimizeCSSAssetsPlugin({}) ], splitChunks: { cacheGroups: { styles: { name: ‘styles‘, test: /\.(css|less)/, chunks: ‘all‘, enforce: true, reuseExistingChunk: true // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。 }, commons: { name: ‘commons‘, chunks: ‘initial‘, minChunks: 2, reuseExistingChunk: true }, vendors: { name: ‘vendors‘, test: /[\\/]node_modules[\\/]/, priority: -10, reuseExistingChunk: true } } }, runtimeChunk: true }, resolve: {...} } 复制代码
安装相关插件:
npm install mini-css-extract-plugin --save-dev 复制代码
配置 mini-css-extract-plugin
插件:
plugins
属性中引入module
的 rules
中使用的 style-loader
替换为 MiniCssExtractPlugin.loader
webpack.config.prod.js
... const MiniCssExtractPlugin = require(‘mini-css-extract-plugin‘); module.exports = { ..., plugins: [ ..., new MiniCssExtractPlugin({ filename: ‘public/styles/[name].[contenthash:8].css‘, chunkFilename: ‘public/styles/[name].[contenthash:8].chunk.css‘ }) ], modules: { rules: [ ..., { test: /\.(css|less)$/, exclude: /node_modules/, use: [ { - loader: ‘style-loader‘ + loader: MiniCssExtractPlugin.loader }, ... ] }, { test: /\.(css|less)$/, exclude: /node_modules/, use: [ { - loader: ‘style-loader‘ + loader: MiniCssExtractPlugin.loader }, ... ] }, ... ] }, ... } 复制代码
webpack.config.prod.js
... module.exports = { ..., plugins: [ ..., new webpack.DefinePlugin({ // 定义 NODE_ENV 环境变量为 production ‘process.env‘: { NODE_ENV: JSON.stringify(‘production‘) } }) ], ... } 复制代码
打包的过程中,由于部分文件名使用的是 hash 值,会导致每次文件不同,因而在 dist 中生成一些多余的文件。所以我们可以在每次打包之前清理一下 dist 目录。
安装插件:
npm install clean-webpack-plugin --save-dev 复制代码
使用插件:
webpack.config.prod.js
..., module.exports = { ..., plugins: [ ..., new CleanWebpackPlugin() ], ... } 复制代码
webpack.config.prod.js
module.exports = { ..., stats: { modules: false, children: false, chunks: false, chunkModules: false }, performance: { hints: false } } 复制代码
运行 npm run build
指令,控制台打包完成之后,根目录下多出了 dist
文件夹。
我这里是用的是 Nginx
作为服务器,发布在本地。
Nginx
下载地址:nginx.org/en/download…。
下载完成之后,解压完成。打开 Nginx
目录,可以找到一个 conf
文件夹,找到其中的 nginx.conf
文件,修改器中的配置:
将图中标注的
html
更换为dist
。
然后我们就可以放心的将打包生成的 dist
文件夹直接放到 Nginx
的安装目录下了。(此时 dist
目录与刚才的 conf
目录应该是同级的)。
启动 Nginx
服务:
start nginx 复制代码
打开浏览器,输入
http://127.0.0.1
或者http://localhost
即可看到我们的项目已经正常的跑起来了。
Nginx 其他命令:
# 停止 Nginx 服务 nginx -s stop # 重启 Nginx 服务 nginx -s reload # 退出 nginx nginx -s quit 复制代码
更多 Ngnix 相关请参考:Nginx官方文档
注意:需要在 Nginx 安装目下执行 nginx 相关命令!
观察 webpack.config.dev.js
和 webpack.config.prod.js
文件,可以发现有大量的代码和配置出现了重复。所以我们可以编写一个 webpack.common.js
文件,将共有的配置放入其中,然后使用 webpack-merge
插件分别引入到 webpack.config.dev.js
和 webpack.config.prod.js
文件中使用。
插件安装:
npm install webpack-merge --save-dev 复制代码
使用:
+ const merge = require(‘webpack-merge‘); + const common = require(‘./webpack.common.js‘); + module.exports = merge(common, { + mode: ‘production‘, + ... + }); 复制代码
这里就展示了一下用法,由于篇幅太长,三个文件中具体的配置代码我就不贴了, 大家可以到 我的 GitHub 上查看一下使用 webpack-merge
后的配置文件。
欢迎Star,谢谢各位!
文章及代码中如有问题,欢迎指正,谢谢!