webpack
介绍
webpack是个工具,协助开发者做项目管理、打包、模块管理、加载资源,并转换到对应环境,webpack是一种静态编译工具(预编译),官网,中文国内
资源: js/css/html/png…/woff/data/vue…
转换:loader
前身
grunt->gulp->browserify->webpack->??
历史
- webpack1 支持CMD和AMD,同时拥有丰富的plugin和loader,webpack逐渐得到广泛应用。
- webpack2 支持ES Module,分析ESModule之间的依赖关系,webpack1必须将ES,Module转换成CommonJS模块,2支持tree sharking
tree sharking: 优化功能,顾名思义,就是将多余的代码给 “摇晃” 掉,在开发中我们经常使用一些第三方库,而这些第三方库只使用了这个库的一部门功能或代码,未使用的代码也要被打包进来,这样出口文件会非常大,tree-sharking 帮我们解决了这个问题,它可以将各个模块中没有使用的方法过滤掉,只对有效代码进行打包
-
webpack3 新的特性大都围绕ES Module提出,如Scope Hoisting和Magic Comment
Scope Hoisting: 可以让 Webpack 打包出来的代码文件更小、运行的更快,它又译作 “作用域提升”,是在 Webpack3 中新推出的功能
Magic Comment: 魔法注释(magic comment)修改打包动态组件名称
weback5
- 尝试用持久性缓存来提高构建性能。
- 尝试用更好的算法和默认值来改进长期缓存。
- 尝试用更好的 Tree Shaking 和代码生成来改善包大小。
- 尝试改善与网络平台的兼容性。
- 尝试在不引入任何破坏性变化的情况下,清理那些在实现 v4 功能时处于奇怪状态的内部结构。
- 试图通过现在引入突破性的变化来为未来的功能做准备,使其能够尽可能长时间地保持在 v5 版本上
目标
模块化,工程化(环境分离),自动化我们的开发环境,涉及到的知识点有入口、出口、转换器、插件
development:
a.浏览器调试工具
b.注释、开发阶段的详细错误日志和提示
c.快速和优化的增量构建机制
production:
a.开启所有的优化代码
b.更小的main大小
c.去除掉只在开发阶段运行的代码
d.Scope hoisting(作用域提升)和Tree-shaking(打包的结构只包含实际用到的 exports)
环境要求
webpack4/5 :Node8+/10、npm5+/6
单页(spa) VS 多页(mpa)
页面模式 | 多页面模式(MPA Multi-page Application) | 单页面模式(SPA Single-page Application) |
---|---|---|
页面组成 | 多个完整页面, 例如page1.html、page2.html等 | 由一个初始页面和多个页面模块组成, 例如:index.html |
公共文件加载 | 跳转页面前后,js/css/img等公用文件重新加载 | js/css/img等公用文件只在加载初始页面时加载,更换页面内容前后无需重新加载 |
页面跳转/内容更新 | 页面通过window.location.href = "./page2.html"跳转 | 通过使用js方法,append/remove或者show/hide等方式来进行页面内容的更换 |
数据的传递 | 可以使用路径携带数据传递的方式,例如:http://index.html?account=“123”&password=123456"",或者localstorage、cookie等存储方式 | 直接通过参数传递,或者全局变量的方式进行,因为都是在一个页面的脚本环境下 |
用户体验 | 如果页面加载的文件相对较大(多),页面切换加载会很慢 | 页面片段间切换较快,用户体验好,因为初次已经加载好相关文件。但是初次加载页面时需要调整优化,因为加载文件较多 |
场景 | 适用于高度追求高度支持搜索引擎的应用 | 高要求的体验度,追求界面流畅的应用 |
转场动画 | 不容易实现 | 容易实现 |
单页面模式:相对比较有优势,无论在用户体验还是页面切换的数据传递、页面切换动画,都可以有比较大的操作空间 多页面模式:比较适用于页面跳转较少,数据传递较少的项目中开发,否则使用cookie,localstorage进行数据传递,是一件很可怕而又不稳定的无奈选择
SPA
安装
全局环境
npm i webpack webpack-cli -g
项目环境
npm i webpack webpack-cli --save-dev
打包
执行: webpack
//默认 会自动找src/index.js 到 dist/main.js
//要求设置开发模式|生产模式
执行: webpack --mode development | production
//指定出入口
webpack 入口地址 --mode development 指定出口 ***
依赖
Webpack 会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到 main.js 。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。在页面启动时,会先执行 main.js 中的代码,其它模块会在运行 require 的时候再执行。
loader
类似一种转化器, 它可以把一个东西,转成另一个,webpack默认只支持javascript文件模块化,其他文件如需模块化,需要转换器(loader,加载器),loader都需要安装
style-loader
编译好的css文件插到页面
css-loader
读取css文件,模块化
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
url-loader
把模块化引入的文件,转换base64
file-loader
模块化引入文件
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 5000,
// outputPath:'images/',//大于5000字节,引文件
name:'images/[name]-[hash:8].[ext]'
}
}
]
}
babel-loader
优雅降级, es6+ 转换 es5,与他同款还有trcuer
安装
npm install babel-loader @babel/core @babel/preset-env -D
@babel/core 核心
@babel/preset-env 解析目标 es6+ 包
配置
webpack .config
module.exports = {
module:{
rules:[
{
test:/\.js$/,
exclude:/node_modules/, 排除
use:[{
loader:'babel-loader',
options:{ //相关配置 可以配置在webpack.config 亦可以配置在.bablerc
presets: ['@babel/preset-env']
}
}]
}
]
}
}
.babelrc
{
"presets": [
"@babel/preset-env"
]
}
配置
命令式
webpack 入口地址 --mode development 指定出口 权重最高
coding
require('style-loader!css-loader!./assets/css/base.css')
配置文件
webpack.config.js 出现在项目根(package.json所在的位置),webpack也可以依赖的第三方的配置文件(bablerc),webpack的配置文件是一个nodejs文件
//是一个nodejs
let path = require('path');
module.exports = {
//入口
entry: './src/main.js',//string object
//出口
output: {//要求必须是个对象,path的值一定要是磁盘路径
path: path.join(__dirname, 'dist'),
publicPath: '/', //公共路径
filename: 'js/app.js'//输出后的文件名
},
//模块
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
},
//模式
mode: 'production', //production 区别环境
}
webpack-dev-server
搭建前端开发环境服务器,可以在开发环境下,浏览器热刷新,模块热重载(增量构建机制),默认自动打包(放到内存),创建前端开发型服务器(默认找webpack.config所在的位置,找index.html),资源查找先找内存,之后是磁盘
配置
//scripts 命令行
"scripts": {
"start": "webpack serve --port 8080 --open --mode development"
},
//webpack.config.js配置文件
devServer:{
port: 8081,//端口
open: false, //开浏览器
// contentBase: path.join(__dirname,'public'), //静态资源|html 托管位置
inline:true, //浏览器热刷新
// host:'127.0.0.1' //主机地址
// https: false , //开启https服务器,需要提供签名文件
// progress: true, //开启打包进度
overlay: true, //浏览器中显示全屏覆盖层。显示编译器错误 V5 有问题
proxy:{
'/api':{
target:'http://localhost:9001',
/* pathRewrite:{
'^/api': ''
} */
},
'/douban':{
target:'https://douban.uieee.com',
pathRewrite:{
'^/douban':''
},
secure: false //接受https的代理
}
},
// watchContentBase: true, //监听public下的文件变动
}
devtool
代码错误没有报到正确位置,使用devtool,把代码错误指定到src源代码中
module.exports={
devtool:'inline-source-map'
}
resolve
定义文件名后缀,省略引入时输入后缀
module.exports={
resolve:{
extensions:['.js','.css','.json','.jsx','.vue']
}
}
定义别名
module.exports={
resolve:{
alias:{
'@': path.resolve(__dirname,'src'),
'~': path.resolve(__dirname,'public')
}
}
}
插件
插件就是一个类|工厂方法,使用需要安装,引入在plugins选项里面实例化
html-webpack-plugin
产出html,动态给html做拷贝,及注入的工作
安装
npm i html-webpack-plugin -S
引入
//webpack.config
var HtmlWebpackPlugin = require('html-webpack-plugin');//插件==类
配置plugins
module.exports = {
plugins:[
new HtmlWebpackPlugin({
// title:'HTML标题',//html 浏览器的标题
filename:'index.html',//输出到dist的文件名
template:'./public/index.html',//要参考的html模板
hash:true, //防止缓存,会给文件后面加入hash
minify:{
//是否去除空格,默认false
collapseWhitespace: true,
//是否压缩html里的css(使用clean-css进行的压缩) 默认值false;
minifyCSS: true,
//是否压缩html里的js(使用uglify-js进行的压缩)
minifyJS: true,
//是否移除注释 默认false
removeComments: true,
},
favicon:'./public/favicon.ico',//配置网址图标
})
]
}
filename 参考 output配置
extract-text-webpack-plugin
css抽离
//npm i extract-text-webpack-plugin@next -D
module.exports = {
module:{
rules:[
/* {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, */
{
test: /\.css$/,
use: ExtractTextWebapckPlugin.extract({ // 接管style-loader
use:'css-loader'
})
},
]
},
plugins:[
new ExtractTextWebapckPlugin('css/[name][hash:6].css') //指向dist下面的css目录
]
}
mini-css-extract-plugin
css抽离
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name]-[hash:3].css",//设定抽离的css存放位置,和文件名
//chunkFilename: "[id].css"//设置片id
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
}
copy-webpack-plugin
资源copy,静态资源(js,数据图片,json) 拷贝到部署环境(生产环境)
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugins:[
new CopyWebpackPlugin({
patterns: [
{ from: path.resolve(__dirname,'public'), to: path.resolve(__dirname,'dist/static') },
],
})
]
}
transfer-webpack-plugin
资源copy,静态资源(js,数据图片,json) 拷贝到部署环境(生产环境)
new TransferWebpackPlugin([//转移文件的插件
{
from: 'assets',
to: 'assets'
}
], path.resolve(__dirname, "src"))
ProvidePlugin
webpack系统插件,自动加载模块,而不必到处 import
或 require
const webpack = require('webpack')
module.exports = {
plugins:[
new webpack.ProvidePlugin({
$: 'jquery',
axios: 'axios'
})
]
}
clean-webpack-plugin
//打包前,清除 dist | build 目录
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins:[
new CleanWebpackPlugin.CleanWebpackPlugin()//清理output指定的 文件夹,在每次打包之前
]
}
SplitChunkPlugin 分片打包
//公共模块拆分 分片打包
optimization: {
//提取公共模块,webpack4去除了CommonsChunkPlugin,使用SplitChunksPlugin作为替代
splitChunks: {
// 表示显示块的范围,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为all;
chunks: "all",
// 表示在压缩前的最小模块大小,默认为0;
minSize: 30000,
//表示被引用次数,默认为1
minChunks: 1,
//最大的按需(异步)加载次数,默认为1;
maxAsyncRequests: 3,
//最大的初始化加载次数,默认为1;
maxInitialRequests: 3,
// 拆分出来块的名字(Chunk Names),默认由块名和hash值自动生成;设置ture则使用默认值
name: true,
//缓存组,目前在项目中设置cacheGroup可以抽取公共模块,不设置则不会抽取
cacheGroups: {
//缓存组信息,名称可以自己定义
app: {
//拆分出来块的名字,默认是缓存组名称+"~" + [name].js
name: "app",
// 同上
chunks: "all",
// 同上
minChunks: 3,
// 如果cacheGroup中没有设置minSize,则据此判断是否使用上层的minSize,true:则使用0,false:使用上层minSize
enforce: true,
//test: 缓存组的规则,表示符合条件的的放入当前缓存组,值可以是function、boolean、string、RegExp,默认为空;
test: /src/
},
//设置多个缓存规则
vendor: {
test: /node_modules/,
chunks: "all",
name: "vendor",
//表示缓存的优先级
priority: 10,
enforce: true
}
}
}
}
配置文件分离
不同的环境(开发,生产,测试),不同的配置文件(dev,prod,test)
物料: webpack-merge 给不同环境合并进去一些公共配置(common|base)
//package.json
//设定 webpack.config -> webpack.config.dev|prod|common|test
"scripts": {
"build": "webpack --mode production --config webpack.config.prod",
"start": "webpack-dev-server --mode development --config webpack.config.dev",
"test": "webpack-dev-server --mode development --config webpack.config.test",
"server": "pm2 start ./xxx --name=8080node --watch"
},
SPA for VUE
基本物料
vue
核心包
vue-loader
支持vue单文件组件
vue-template-compiler
编译时使用
webpack
工具
webpack-dev-server
开发服务器
可选
style-loader
| vue-style-loader
把样式插入到style标签
sass-loader
node-sass
支持scss
bable-loader
| @babel/core
| @babel-preset-env
支持es6+
url-loader
| file-loader
文件优化
配置
问题
- 处理资源路径:前景图片 相对路径
- 支持 async await
- css模块化: 开启模块化会影响所有vue文件
SPA FOR REACT
一、 初始化项目
- 创建项目目录
- 初始化 yarn 项目
yarn init -y
- 初始化 git 项目
# 初始化项目
git init
# 添加 .gitignore
echo "/node_modules\n/build" >> .gitignore
# 关联远程仓库
git remote add origin <url>
二、 Webpack 配置
2.1 基础配置设置
- 创建文件
/src/index.js
作为 webpack 的入口文件
import React from 'react';
import reactDom from 'react-dom';
const App = () => (
<div>
test page
</div>
);
reactDom.render(<App/>, document.getElementById('root'));
- 创建模板文件
/public/index.html
webpack 打包后的文件将添加到该文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</meta></meta></head>
<body>
<div id="root"></div>
</body>
</html>
- 创建
webpack
开发环境下配置文件/config/webpack.config.dev.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlWebpackPlugin = new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
});
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, '../src/index.js'),
output: {
path: path.resolve(__dirname, '../build'),
filename: 'js/[name].[hash].bundle.js',
},
module: {
rules: [
{
test: /\.(mjs|js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
}
],
},
plugins: [
htmlWebpackPlugin,
],
resolve: {
extensions: ['.mjs', '.js', '.jsx'],
},
};
- 创建
webpack
生产环境下配置文件/config/webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlWebpackPlugin = new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
});
module.exports = {
mode: 'production', // 和开发环境下的配置只是修改了 mode
entry: path.resolve(__dirname, '../src/index.js'),
output: {
path: path.resolve(__dirname, '../build'),
filename: 'js/[name].[hash].bundle.js',
},
module: {
rules: [
{
test: /\.(mjs|js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
}
],
},
plugins: [
htmlWebpackPlugin,
],
resolve: {
extensions: ['.mjs', '.js', '.jsx'],
},
};
- 创建
babel
配置文件.babelrc
{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
]
}
- 修改
package.json
: 添加 npm 脚本
"scripts": {
+ "start": "webpack-dev-server --config ./webpack/webpack.config.dev.js --open",
+ "build": webpack --config ./webpack/webpack.config.prod.js"
}
2.2 安装基础插件包
webpack 相关依赖包、插件
- webpack: webpack 基础包
- webpack-cli: webpack cli 工具包
- html-webpack-plugin: webpack 插件, 用于将打包后的文件添加到指定的 html 内
- webpack-dev-server: webpack 开发环境工具, 创建一个开发环境
- babel-loader: webpack loader, 用于编译打包 js 文件
- @babel/core: babel 依赖包, 将 js 代码分析成 ast
- @babel/preset-react: webpack react 相关预设
- @babel/preset-env: weboack react 相关预设, 这样就可以使用最新的 js 相关语法
yarn add webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader @babel/core @babel/preset-react @babel/preset-env --save-dev
react 相关依赖包
- react
- react-dom
yarn add react react-dom --save
2.3 测试
- 执行
yarn start
测试项目是否能够正常运行 - 执行
yarn build
测试是否能够正常对项目进行打包、编译, 编译后目录结构如下
.
├── index.html
└── js
└── main.0b16f9b82b7fb2c9ba47.bundle.js
2.4 总结
到这里一个最基本的 React 项目就已经搭建起来了, 但如果只是这些配置简单配置肯定是远远不够的, 上面只是为了说明其实要搭建一个 React 基础项目还是很简单的, 剩下的工作就是不断的根据具体需求扩充项目配置。下面开始会简单根据需要对项目的配置进行扩展, 比如:
- webpack 添加对 scss 样式文件的解析
- webpack 添加对图片的解析
- 项目添加 eslint 配置
- 项目添加版本发布、git commit 规范配置
- 项目添加对 antd 的支持, 并配置按需加载模块
三、 添加对 scss 样式文件的支持
3.1 TODO
- 添加对 css 样式文件的支持
- 添加对 scss 样式文件的支持
- 使用 mini-css-extract-plugin 将 css抽离
- 添加 css 模块化的支持, 对
.module.css``.module.scss
的样式文件启用模块化
3.2 webpack 配置修改
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+ const miniCssExtractPlugin = new MiniCssExtractPlugin({
+ filename: 'style/[name].[hash].css',
+ chunkFilename: 'style/[id].[hash].css',
+ });
+ const cssRegex = /\.(css|scss)$/;
+ const cssModuleRegex = /\.module\.(css|scss)$/;
module.exports = {
module: {
rules: [
+ {
+ test: cssRegex,
+ exclude: cssModuleRegex,
+ sideEffects: true,
+ use: [
+ {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ hmr: process.env.NODE_ENV === 'development',
+ },
+ },
+ { loader: 'css-loader', options: { importLoaders: 1 } },
+ 'sass-loader',
+ ],
+ },
+ {
+ test: cssModuleRegex,
+ use: [
+ {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ hmr: process.env.NODE_ENV === 'development',
+ },
+ },
+ {
+ loader: 'css-loader',
+ options: {
+ modules: {
+ localIdentName: '[local]__[hash:base64]',
+ },
+ },
+ },
+ 'sass-loader',
+ ],
+ }
],
},
plugins: [
+ miniCssExtractPlugin,
],
};
3.3 安装依赖
- css-loader
- sass-loader
- node-sass
- mini-css-extract-plugin
yarn add mini-css-extract-plugin css-loader sass-loader node-sass --save-dev
3.4 代码测试
创建 src/index.css
.css {
padding-top: 20px;
}
创建 src/index.module.css
.module-css {
padding-right: 20px;
}
创建 src/index.scss
.scss {
padding-bottom: 20px;
}
创建 src/index.module.scss
.module-scss {
padding-left: 20px;
}
修改 src/index.js
import React from 'react';
import reactDom from 'react-dom';
+ import css from './index.module.css';
+ import scss from './index.module.scss';
+ import './index.css';
+ import './index.scss';
const App = () => (
+ <div className="{`" +="" css="" scss="" ${css['module-css']}="" ${scss['module-scss']}="" `}="">
test page
</div>
);
reactDom.render(<App/>, document.getElementById('root'));
运行项目测试样式是否正确加载
四、 添加对图片的支持
这里其实没什么好讲的, 主要使用
url-loader
对图片进行处理, 需要特别注意的是该插件依赖于file-loader
4.1 webpack 修改
module.exports = {
module: {
rules: [
+ {
+ test: /\.(png|jpg|gif|woff|svg|eot|ttf)$/,
+ use: [{
+ loader: 'url-loader',
+ options: {
+ limit: 10000,
+ name: 'assets/[hash].[ext]',
+ },
+ }],
+ },
],
},
};
4.2 依赖安装
yarn add url-loader file-loader --save-dev
4.3 测试
没什么好说的, 找一张图片在 src/index.js
中引用看是否能够正常显示即可
import React from 'react';
import reactDom from 'react-dom';
import Img from './1519971416-JDHjSqWCph.jpg';
const App = () => (
<div>
<img src="{Img}"/>
</div>
);
reactDom.render(<App/>, document.getElementById('root'));
五、 esling 配置
5.1 webpack 配置修改
这里只需在 babel-loader
之后添加 eslint-loader
, 需要特别注意的是它们的顺序
module.exports = {
module: {
rules: [
{
test: /\.(mjs|js|jsx)$/,
exclude: /node_modules/,
+ use: ['babel-loader', 'eslint-loader'],
},
],
},
};
5.2 项目下添加 .eslintrc.js
配置文件
module.exports = {
parser: 'babel-eslint',
// 开发环境设置: 在使用下拉环境中的全局变量时不会因为未定义变量而报错, 如 window
env: {
browser: true,
node: true
},
// 定义全局变量, 在直接使用下列全局变量情况下不会因为未定义变量而报错
globals: {
_: true,
lodash: true,
},
// 插件列表
plugins: [
'react',
'import',
'react-hooks',
],
// 继承的规则
extends: [
'eslint:recommended',
'plugin:react/recommended',
],
// 自定义规则列表
rules: {
// 强制在每个语句后面使用分号
"semi": [1, "always"],
}
}
5.3 项目下添加 .eslintignore
# 设置需要忽略的文件
/src/assets/*
5.4 安装依赖
- eslint
- babel-eslint
- eslint-loader
- eslint-plugin-import
- eslint-plugin-react
- eslint-plugin-react-hooks
yarn add eslint babel-eslint eslint-loader eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks -D
5.5 测试
修改 src/index.js
import React from 'react';
import reactDom from 'react-dom';
const App = () => (
<div>
test page
</div>
)
reactDom.render(<App/>, document.getElementById('root'));
重新运行项目, 如果配置正常则会抛出警告
5.6 添加 yarn 脚本, 用于自动修复部分语法不规范的代码
"scripts": {
+ "eslint:fix": "eslint --fix ./src"
},
通过执行 yarn eslint:fix
则会修复项目中部分能够自动修复的不规范代码
六、 引入 Antd 并配置按需加载
这里主要为 .babelrc
配置添加插件 babel-plugin-import
从而实现 antd 的按需加载
6.1 修改 .babelrc
说明: 配置插件时可以设置实例化名称 import-antd
, 这样就可以多次使用同一插件, 如果你还需要使用 babel-plugin-import
处理其他组件库
{
+ "plugins": [
+ ["import", {
+ "libraryName": "antd",
+ "libraryDirectory": "es",
+ "style": "css"
+ }, "import-antd"]
+ ],
"presets": [
"@babel/preset-react",
"@babel/preset-env"
]
}
6.2 依赖安装
npm i antd -S
npm i babel-plugin-import -D
6,3 测试
在 src/index
中引用样式, 测试是否能够正常使用
import React from 'react';
import reactDom from 'react-dom';
+ import { Button } from 'antd';
const App = () => (
<div>
+ <Button type="primary">按钮</Button>
</div>
);
reactDom.render(<App/>, document.getElementById('root'));
七、 版本发布、git commit 规范校验配置
这一节节选我另一篇文章 commit 规范校验配置和版本发布配置
, 下面只介绍简单配置方法
7.1 依赖包安装
//husky 包安装
yarn add husky --save-dev
//commitlint 所需包安装
yarn add @commitlint/config-angular @commitlint/cli --save-dev
//commitizen 包安装
yarn add commitizen --save-dev
yarn add commitizen -g
//standard-version 包安装
yarn add standard-version --save-dev
7.2 配置 commitlint 和 commitizen
//生成 commitlint 配置文件
echo "module.exports = {extends: ['@commitlint/config-angular']};" > commitlint.config.js
//commitizen 初始化
commitizen init cz-conventional-changelog --save-dev --save-exact
7.3 更新 package.json
脚本说明:
- release: 自定义要发布的版本, 如:
npm run release -- 1.0.0
- release💯 执行该脚本, 那么如果当前版本是 1.0.0 那么版本将被提升至 2.0.0
- release:010: 执行该脚本, 那么如果当前版本是 1.0.0 那么版本将被提升至 1.1.0
- release:001: 执行该脚本, 那么如果当前版本是 1.0.0 那么版本将被提升至 1.0.1
{
"scripts": {
+ "commit": "git-cz",
+ "release": "standard-version --release-as",
+ "release:100": "npm run release -- major",
+ "release:010": "npm run release -- minor",
+ "release:001": "npm run release -- patch",
},
+ "husky": {
+ "hooks": {
+ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
+ }
+ }
}
7.4 commit 方式
- 全局安装 commitizen 情况下可使用
git cz
或者npm run commit
来提交代码 - 未全局安装 commitizen 情况下可使用
npm run commit
来提交代码
7.5 版本发布流程
//1. 切换到指定分支
git checkout master
//2. 更新代码
git pull origin master
//3. 版本发布: 生成 CHANGELOG.md 并创建 tag
npm run release -- --release-as 1.0.0
//4. 更新 tag 至远程分支
git push --follow-tags origin master
八、 更多配置
8.1 webpack 拷贝 public 文件
+ const CopyWebpackPlugin = require('copy-webpack-plugin');
+ const copyWebpackPlugin = new CopyWebpackPlugin(
+ [{ from: path.resolve(__dirname, '../public') }]
+ );
module.exports = {
plugins: [
+ copyWebpackPlugin,
]
};
8.2 定义全局变量
+ const { DefinePlugin } = require('webpack');
+ const definePlugin = new DefinePlugin({
+ _DEV_: false,
+ GLOBAL_SERVICE: {
+ HOST: JSON.stringify('https://www.qianyin925.com:4000'),
+ GRAPHQL_URL: JSON.stringify('/graphql'),
+ },
+ });
module.exports = {
plugins: [
+ definePlugin,
]
};
8.3 自动加载依赖
+ const { ProvidePlugin } = require('webpack');
+ const providePlugin = new ProvidePlugin({
+ _: 'lodash',
+ lodash: 'lodash',
+ });
module.exports = {
plugins: [
+ providePlugin,
]
};
8.4 webpack 定义路径别名
module.exports = {
resolve: {
+ alias: config.alias || {},
},
};
8.5 cross-env 设置环境变量
优点: 兼容多个平台
{
"scripts": {
+ "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
}
}
8.6 raw-loader 用于加载文本内容(.txt、.md 文件)
module.exports = {
module: {
rules: [
{
test: /\.(text|md)$/,
use: 'raw-loader',
},
]
}
};
MPA
多页面:多入口(entry),多出口(output),多HTML输出(HtmlWebpackPlugin)
module.exports={
entry: //入口接收string | json
{app:'index1.js',app2:'index2.js'} 输出要求多输出
output: {
path:path.resolve(__dirname,'dist') //指定编译目录
publicPath:'/', //公共路径 影响资源(图)路径,devserver路径,多页资源路径,开发生产环境基础资源路径,cdn路径
filename:'js/main.js' 单文件输出 | 'js/[name].js' 多输出 html引入app和app2 配合
}
plugins:[
new HtmlWebpackPlugin({关于生成app.html配置}),
new HtmlWebpackPlugin({关于生成app2.html配置}),
]
}
vue-cli4 for MPA
思想
vue的多页面,就是多实例(vue),一个html一个vue实例
创建项目
vue create 目录
项目目录组织
public //每一个html都来自index.html的拷贝
|- index.html id=index
|- page1.html id=page1
|- pageN.html ...
|- 无需优化的公共资源
pages
|- index
|- index.js 主入口
|- index.vue 根组件件
|- page1
|- page1.js 主入口 对等 index.js入口
|- page2.vue 根组件件
...
components
|- 通用组件.vue
assets
|- 通用资源 需要优化
vue.config.js 配置多页面
配置
module.exports = {
pages: {
index: {
// 应用入口配置,相当于单页面应用的main.js,必需项
entry: 'src/pages/index/index.js',
// 应用的模版,相当于单页面应用的public/index.html,可选项,省略时默认与模块名一致
template: 'public/index.html',
// npm run build 编译后在dist目录的输出文件名,可选项,省略时默认与模块名一致
filename: 'index.html',
// 标题,可选项,一般情况不使用,通常是在路由切换时设置title
// 需要注意的是使用title属性html文件 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: 'index page config',
// 包含的模块,可选项
// chunks: ['index']
},
// 只有entry属性时,直接用字符串表示模块入口
page1: {entry:'src/pages/page1/page1.js',title:'page1 config'},
page2: {entry:'src/pages/page2/page2.js',title:'page2 config'},
// page3: 'src/pages/page3/page3.js',
}
}
create-react-app for MPA
方案1
方案2
创建
create-react-app 目录
解构配置
yarn eject
配置目录结构
public //每一个html都来自index.html的拷贝
|- index.html
|- page1.html
|- pageN.html
|- 无需优化的公共资源
pages
|- index
|- index.js 主入口
|- index.jsx 根组件件
|- order
|- index.jsx 主入口 对等 index.js入口
|- App.jsx 根组件件
...
components
|- 通用组件.vue
assets
|- 通用资源 需要优化
config // eject出来的配置
scripts
配置webpack
在config/webpack.config 配置多入口
//原始
entry:[
paths.appIndexJs,
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')
].filter(Boolean),
//修改后的
entry:{
index: [paths.appIndexJs, isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')].filter(Boolean),
query: [paths.appQueryJs, isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')].filter(Boolean),
ticket: [paths.appTicketJs, isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')].filter(Boolean),
order: [paths.appOrderJs, isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')].filter(Boolean),
}
多入口文件的路径(paths.appIndexJs)写在paths文件中,到config/paths中添加这几个变量
//原始
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index/index'),
//改后
appHtml: resolveApp('public/index.html'),
appQuery: resolveApp('public/query.html'),
appTicket: resolveApp('public/ticket.html'),
appOrder: resolveApp('public/order.html'),
appIndexJs: resolveModule(resolveApp, 'src/index/index'),
appQueryJs: resolveModule(resolveApp, 'src/query/index'),
appTicketJs: resolveModule(resolveApp, 'src/ticket/index'),
appOrderJs: resolveModule(resolveApp, 'src/order/index'),
多html的产出,在config/webpack.config.js 去配置多个HtmlWebpackPlugin
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true, //允许注入内容到html 在打包输出时
template: paths.appHtml, //参考模板 指向paths里面的配置
filename: 'index.html', //输出的html的文件名
chunks: ['index'],
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
new ...,
new ...
可以考虑,把入口和出口,模板html 做一个动态生成,推荐node包(glob)
报错 filter错误时
//config/webpack.config.js
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
return {
files: manifestFiles,
entrypoints: entrypointFiles, //干掉这一行
};
},
})
ManifestPlugin这是针对单一入口的配置,因为单一入口不指定name的情况默认name为main,当改成多入口的方式了之后这里面在entrypoints中自然是读取不到main这个值的,因此就报错,这里将generate这个参数去掉,恢复其默认值即可,或者将entrypoints这个key去掉