初始化依赖代码:https://github.com/wanglifa/webpack-config-demo1
1.让webpack支持IE
直接在我们的.browserslistrc 文件里写下面的代码
[production]
> 1%
ie 9
[modern]
last 1 chrome version
last 1 firefox version
[ssr]
node 12
2.让 webpack 支持 JSX
安装一个 babel-loader 和 preset-react 然后再 webpack.config.js 里写
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react']
]
}
}
}
]
},
}
这时候运行 yarn build 会发现有个报错
解决方法:在config里添加
optimization: {
minimize: false
}
创建 jsx
const JsxDemo = () => {
return (
<div>jsxdemo</div>
)
}
export { JsxDemo }
问题:上面的jsx 代码我们没有引入 react 也没有报错
3.配置 eslint 插件
3.1. 编辑器的 eslint
.eslintrc.js
module.exports = {
extends: ['react-app'], // 继承react-app 的规则
rules: {
'react/jsx-uses-react': [2], // 0 没有使用的情况下不管它, 1 是警告 2是报错
// 提示要在 JSX 文件里手动引入 React
'react/react-in-jsx-scope': [2]
}
}
3.2. webpack 的 eslint
plugins: [
new ESLintPlugin({
extensions: ['.js', '.jsx']
})
],
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
+ ['@babel/preset-react', {runtime: 'classic'}],
]
}
}
}
]
},
4.使用 babel-loader 打包 TS
- webpack.config.js
rules: [
{
+ test: /\.[jt]sx?$/, // t或j开头
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
+ ['@babel/preset-typescript']
]
}
}
}
]
5.让 Eslint 支持 ts
- eslint.js
overrides: [{
files: ['*.ts', '*.tsx'],
parserOptions: {
project: './tsconfig.json', // ts文件根配置,不加会报错
},
extends: ['airbnb-typescript'], // package.json 里安装的 eslint-config-airbnb-typescript
rules: {
'@typescript-eslint/object-curly-spacing': [0],
'import/prefer-default-export': [0],
}
}]
- webpack.config.js
plugins: [new ESLintPlugin({
extensions: ['.js', '.jsx', '.ts', '.tsx'] // 不加 .jsx 就不会检查 jsx 文件了
})],
6.支持打包 tsx
首先我们需要初始化一个tsconfig.json 文件
使用 npx tsc --init
然后修改默认参数
- tsconfig.json
"jsx": "react",
"strict": false, // false 不对全局类型做检查
"noImplicitAny": true, // true 不允许有隐式的any
7.让 JS 和 TS 支持 @
js配置
- webpack.config.js
const path = require('path')
resolve: {
alias: {
// 当前文件下面的src替换为@路径
'@': path.resolve(__dirname, './src/')
}
},
ts 配置
- tsconfig.json
"baseUrl": ".",
"paths": {
"@/*": ["src/*"] // 任何一@/的引入都会等价于src/
},
8.支持 SCSS
-webpack.config.js
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
上面代码的顺序是先执行sass-loader 然后执行 css-loader 最后执行 style-loader
在 scss 文件里只需要在webpack里配置了别名后,直接路径前加一个~就可以使用别名了
@import "~@/scss-vars.scss";
8.1.让 scss 自动的引入全局 scss 文件
正常情况下一个全局的 scss 文件我们需要在每一个 scss 文件里都引入一下这个全局的,但是这样写太麻烦了,所以我们可以通过webpack配置
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
additionalData: `
@import "src/scss-vars.scss"; //每个scss文件里都添加这个 scss 文件
`,
sassOptions: {
includePaths: [__dirname]
},
},
},
],
},
8.2. 让 js 可以读取 scss 文件里的变量
场景:js 操作 dom 给某一元素添加 css 属性,需要从 scss 文件中获取变量
1). 创建两个文件一个是 scss 需要 共享给 js 的变量的声明,一个文件是导出这些变量
- scss-vars.scss
$color: blue;
- scss-exports.scss
:export {
color: $color;
}
2). 修改 webpack.config.js 里的 css-loader
{
loader: 'css-loader',
options: {
modules: {
compileType: 'icss',
},
},
},
9. 支持 LESS
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
}
],
},
9.1. LESS 全局自动引入
- webpack.config.js
{
loader: 'less-loader',
options: {
additionalData: `
@import "~@/less-vars.less";
`,
},
}
9.2. JS 读取 LESS 里的变量
同 SCSS 一样,但是LESS 可以把变量声明和导出放在一个文件下,SCSS 放在一个文件下会有问题
- webpack.config.js
{
loader: 'css-loader',
options: {
modules: {
compileType: 'icss',
},
},
},
- less-vars.less
@color: red;
:export {
color: @color;
}
- index.js
import vars from '@/less-demo.less'
console.log(vars) // {color: 'red'}
10.支持 styl 文件
{
test: /\.styl(us)?$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
compileType: 'icss',
},
},
},
{
loader: 'stylus-loader',
options: {
stylusOptions: {
import: [path.resolve(__dirname, 'src/stylus-vars.styl')]
}
},
}
],
},
- stylus-vars.style
color = red;
:export {
color: color;
}
11. 打包 css 文件并给文件加 hash
11.1. 打包 css
安装 mini-css-extract-plugin,配置 webpack.config.js
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const cssLoaders = (loaders) => [
// 'style-loader',
MiniCssExtractPlugin.loader,
...
]
module.exports = {
plugins: [
new ESLintPlugin({
extensions: ['.js', '.jsx', '.ts', '.tsx']
}), new MiniCssExtractPlugin()
],
}
运行 yarn build 我们打包生成的 dist 目录下会出现一个 mian.css 文件
11.2. 给打包出来的css加 hash后缀
在MiniCssExtractPlugin 里添加一个 filename
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
给 js 文件添加 hash
output: {
filename: '[name].[contenthash].js'
},
11.3. 只针对生产环境打包 css
const mode = 'production'
const cssLoaders = (loaders) => [
mode === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
...
]
module.exports = {
mode,
...
}
12. 自动生成 HTML
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode,
plugins: [
...
mode === 'production' && new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
}),
new HtmlWebpackPlugin()
].filter(Boolean),
...
}
上面代码如果 mode === 'production' 是 false 的话就会报错,所以我们使用 filter(Boolean) 过滤 false项
13. webpack 优化
13.1 runtime 单独打包
// 优化
optimization: {
// 运行时文件单独打包
+ runtimeChunk: 'single',
}
13.1.1 runtime
让一个东西运行而提供的代码就是 runtime
为什么 runtime
需要单独打包?
如果不单独打包当我们修改了配置或者事 webpack 升级了后,运行 yarn build
可能我们的 runtime
就会变,因为我们 runtime
默认是包含在 main.js
里的,所以 main.js
就会变,然后它的 hash
就会变,用户之前缓存的 main.xxx.js
就会失效,比如我们之前是 main.xxx.js
build 后就会变成 main.yyy.js
,用户就得额外多下载一个 main.yyy.js
文件,而我们项目本身的代码是没有变的所以我们不希望我们打包的 mian.js
变化,如果单独打包 runtime 的话,我们额外的 js 都会打包到 runtime 里,只要我们 Index.js 的内容没变 build 后就会一直是之前的 main.xxx.js
13.2. node 依赖单独打包
比如让我们的 react 单独打包
optimization: {
// 拆分文件
splitChunks: {
cacheGroups: {
vendor: {
minSize: 0, /* 如果不写 0,由于 React 文件尺寸太小,会直接跳过 */
test: /[\\/]node_modules[\\/]/, // 为了匹配 /node_modules/ 或 \node_modules\
name: 'vendors', // 文件名
chunks: 'all', // all 表示同步加载和异步加载,async 表示异步加载,initial 表示同步加载
// 这三行的整体意思就是把两种加载方式的来自 node_modules 目录的文件打包为 vendors.xxx.js
// 其中 vendors 是第三方的意思
},
},
},
}
13.3. 固定 moduleIds
为了保证用户不重复下载没有变化的内容,所以我们需要固定 modukeIds,我们现在打包的文件里有个 905******.js,如果我们还有一个js 可能会生成一个 403****.js 每次生成的文件都是随机的,如果我们把某一个 js 删了,就有可能导致其他所有模块的 id 都重新变化,那么我们生成的文件名就又会变了,用户又得重新下载
optimization: {
moduleIds: 'deterministic',
}
14. webpack 多页面配置
因为我么的 html 页面是通过 new HtmlWebpackPlugin() 生成的,那么我们想有两个 html 页面只要有两个 new HtmlWebpackPlugin() 就可以,但是我们的 new HtmlWebpackPlugin() 默认生成的页面就是index.html 我们这样写两个其实也只有一个 index.html,所以我们需要加配置
new HtmlWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'admin.html',
})
上面这样写虽然我们能额外生成一个 admin.html 但是它里面引入的js 还是我们 index.html 里的 js,所以我们还需要指定我们的 js
new HtmlWebpackPlugin({
filename: 'admin.html',
chunks: ['admin']
})
但是这样还是不行,因为webpack 默认只会打包 Index.js,所以我们还需要配置多入口
entry: {
main: './src/index.js',
admin: './src/admin.js'
}
这里如果我们不在默认的 new HtmlWebpackPlugin 里也就是 index.html 里添加配置的话,它也会把我们 admin.js 引入进入,所以如果我们想只让它引入 main.js 那么我们也要加chunks: ['main']
14.1. 多页面优化
我们现在有两个页面,每个页面都有一个js文件,如果我们这两个js里都引入同样的 js 代码,那么我们打包生成的这两个 js 文件里每一个都会有我们引入的相同的js代码,那么我们的代码体积就会非常大,而且每个js里都会有重复的代码,所以我们需要把重复的代码单独打包成一个 common.js
common: {
priority: 5, // 优先级
minSize: 0,
minChunks: 2, // 只要被两个文件引用就会放到 common 里
chunks: "all",
name: 'common',
}
比如
- share.js
const shared = '公共的'
export {shared}
- index.js
import {shared} from './share.js'
- admin.js
import {shared} from './share.js'
打包后我们的 share.js 里的内容就会打包到 common.js 里
网友评论