├── .eslintignore
├── src
├── style
│ ├── reset.css
│ └── base.scss
├── assets
│ ├── logo.png
│ └── man.jpg
├── App.vue
└── index.js
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── webpack.analysis.config.js
├── .babelrc
├── commitlint.config.js
├── index.html
├── webpack.dll.config.js
├── webpack.prod.config.js
├── package.json
├── webpack.dev.config.js
├── webpack.base.config.js
├── vendor-manifest.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist/
2 | /node_modules/
3 | /*.js
4 |
--------------------------------------------------------------------------------
/src/style/reset.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NuoHui/webpack-demo/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/man.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NuoHui/webpack-demo/HEAD/src/assets/man.jpg
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'plugin:vue/essential'
4 | ],
5 | rules: {
6 | },
7 | parserOptions: {
8 | parser: "babel-eslint",
9 | ecmaVersion: 2017
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | dist/
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | vue测试
4 |
5 |
6 |
11 |
17 |
--------------------------------------------------------------------------------
/webpack.analysis.config.js:
--------------------------------------------------------------------------------
1 | const merge = require('webpack-merge')
2 | const prodWebpackConfig = require('./webpack.prod.config.js')
3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
4 |
5 | module.exports = merge(prodWebpackConfig, {
6 | plugins: [
7 | new BundleAnalyzerPlugin() // 打包分析
8 | ]
9 | })
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import './style/reset.css'
3 | import './style/base.scss'
4 |
5 | import Vue from 'vue'
6 | import App from './App.vue'
7 | import ElementUI from 'element-ui'
8 |
9 | Vue.use(ElementUI)
10 |
11 | // eslint-disable-next-line
12 | new Vue({
13 | el: '#app',
14 | render: h => h(App)
15 | })
16 | window._ = _
17 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env"
5 | ]
6 | ],
7 | "plugins": [
8 | [
9 | "@babel/plugin-transform-runtime",
10 | {
11 | "corejs": false,
12 | "helpers": true,
13 | "regenerator": true,
14 | "useESModules": false,
15 | "absoluteRuntime": "@babel/runtime"
16 | }
17 | ]
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/style/base.scss:
--------------------------------------------------------------------------------
1 | $bd-bg: pink;
2 | body {
3 | background: $bd-bg;
4 | display: flex;
5 | .logo {
6 | background: url('../assets/logo.png') no-repeat;
7 | width: 100px;
8 | height: 100px;
9 | background-size: contain;
10 | }
11 | .man {
12 | background: url('../assets/man.jpg') no-repeat;
13 | width: 40px;
14 | height: 40px;
15 | background-size: contain;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 配置commit规则
3 | build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
4 | ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
5 | docs:文档更新
6 | feat:新增功能
7 | merge:分支合并 Merge branch ? of ?
8 | fix:bug 修复
9 | perf:性能, 体验优化
10 | refactor:重构代码(既没有新增功能,也没有修复 bug)
11 | style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
12 | test:新增测试用例或是更新现有测试
13 | revert:回滚某个更早之前的提交
14 | chore:不属于以上类型的其他类型
15 | */
16 | module.exports = {
17 | extends: ['@commitlint/config-conventional']
18 | }
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 | <%= htmlWebpackPlugin.options.title %>
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/webpack.dll.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const TerserPlugin = require('terser-webpack-plugin')
4 |
5 | module.exports = {
6 | mode: 'production',
7 | // 你想要打包的模块的数组
8 | entry: {
9 | vendor: ['vue/dist/vue.min.js', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
10 | },
11 | output: {
12 | path: path.join(__dirname, 'static/js'), // 打包后文件输出的位置
13 | filename: '[name].dll.js',
14 | library: '[name]_library'
15 | // vendor.dll.js中暴露出的全局变量名。
16 | // 主要是给DllPlugin中的name使用,
17 | // 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
18 | },
19 | plugins: [
20 | new webpack.DllPlugin({
21 | path: path.join(__dirname, '.', '[name]-manifest.json'),
22 | name: '[name]_library',
23 | context: __dirname
24 | })
25 | ],
26 | optimization: {
27 | minimizer: [
28 | // 压缩JS
29 | new TerserPlugin({
30 | cache: true,
31 | parallel: true,
32 | sourceMap: false
33 | })
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
3 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin')
4 | const TerserPlugin = require('terser-webpack-plugin')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.config.js')
7 | const devMode = process.env.NODE_ENV !== 'production'
8 |
9 | module.exports = merge(baseWebpackConfig, {
10 | mode: 'production',
11 | output: {
12 | filename: 'bound.[hash:5].js',
13 | path: path.resolve(__dirname, 'dist')
14 | },
15 | module: {
16 | rules: [
17 | // 处理css/scss/sass
18 | {
19 | test: /\.(sc|sa|c)ss$/,
20 | use: [
21 | MiniCssExtractPlugin.loader,
22 | {
23 | loader: 'css-loader'
24 | },
25 | {
26 | loader: 'postcss-loader',
27 | options: {
28 | ident: 'postcss',
29 | plugins: (loader) => [
30 | require('autoprefixer')({
31 | browsers: [
32 | 'last 10 Chrome versions',
33 | 'last 5 Firefox versions',
34 | 'Safari >= 6',
35 | 'ie > 8'
36 | ]
37 | })
38 | ]
39 | }
40 | },
41 | {
42 | loader: 'sass-loader'
43 | }
44 | ]
45 | }
46 | ]
47 | },
48 | plugins: [
49 | // 提取CSS
50 | new MiniCssExtractPlugin({
51 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名
52 | chunkFilename: devMode ? '[id].css' : '[id].[hash:5].css'
53 | })
54 | ],
55 | optimization: {
56 | minimizer: [
57 | // 压缩JS
58 | new TerserPlugin({
59 | cache: true,
60 | parallel: true,
61 | sourceMap: true
62 | // 等等详细配置见官网
63 | }),
64 | // 压缩CSS
65 | new OptimizeCSSAssertsPlugin({})
66 | ]
67 | }
68 | })
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-demo",
3 | "version": "1.0.0",
4 | "description": "webpack4 demo",
5 | "main": "index.js",
6 | "dependencies": {
7 | "axios": "^0.18.0",
8 | "core-js": "^2.5.7",
9 | "element-ui": "^2.8.2",
10 | "vue": "^2.6.10",
11 | "vue-router": "^3.0.6",
12 | "vuex": "^3.1.1",
13 | "webpack": "^4.30.0"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.4.4",
17 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
18 | "@babel/plugin-transform-runtime": "^7.4.4",
19 | "@babel/preset-env": "^7.4.4",
20 | "@babel/runtime": "^7.4.4",
21 | "@commitlint/cli": "^7.6.0",
22 | "@commitlint/config-conventional": "^7.6.0",
23 | "add-asset-html-webpack-plugin": "^3.1.3",
24 | "autoprefixer": "^9.5.1",
25 | "babel-eslint": "^10.0.1",
26 | "babel-loader": "^8.0.5",
27 | "clean-webpack-plugin": "^1.0.1",
28 | "cross-env": "^5.2.0",
29 | "css-loader": "^2.1.1",
30 | "eslint": "^5.16.0",
31 | "eslint-loader": "^2.1.2",
32 | "eslint-plugin-vue": "^5.2.2",
33 | "file-loader": "^3.0.1",
34 | "html-webpack-plugin": "^3.2.0",
35 | "husky": "^2.2.0",
36 | "image-webpack-loader": "^4.6.0",
37 | "lodash": "^4.17.11",
38 | "mini-css-extract-plugin": "^0.5.0",
39 | "node-sass": "^4.12.0",
40 | "optimize-css-assets-webpack-plugin": "^5.0.1",
41 | "postcss-import": "^12.0.1",
42 | "postcss-loader": "^3.0.0",
43 | "sass-loader": "^7.1.0",
44 | "standard": "^12.0.1",
45 | "style-loader": "^0.23.1",
46 | "terser-webpack-plugin": "^1.2.3",
47 | "uglifyjs-webpack-plugin": "^2.1.2",
48 | "url-loader": "^1.1.2",
49 | "vue-loader": "^15.7.0",
50 | "vue-style-loader": "^4.1.2",
51 | "vue-template-compiler": "^2.6.10",
52 | "webpack-bundle-analyzer": "^3.3.2",
53 | "webpack-cli": "^3.3.1",
54 | "webpack-dev-server": "^3.3.1",
55 | "webpack-merge": "^4.2.1"
56 | },
57 | "scripts": {
58 | "dll": "webpack --config ./webpack.dll.config.js",
59 | "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/",
60 | "dev": "cross-env NODE_ENV=development npx webpack-dev-server --open --progress --config webpack.dev.config.js",
61 | "build": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js",
62 | "build:report": "cross-env NODE_ENV=production npx webpack --progress --config webpack.analysis.config.js"
63 | },
64 | "keywords": [
65 | "webpack4",
66 | "demo"
67 | ],
68 | "husky": {
69 | "hooks": {
70 | "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS",
71 | "pre-commit": "eslint \"src/**/*.{js,ts,vue}\""
72 | }
73 | },
74 | "author": "xyz",
75 | "license": "MIT"
76 | }
77 |
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const merge = require('webpack-merge')
3 | const baseWebpackConfig = require('./webpack.base.config.js')
4 | const webpack = require('webpack')
5 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
6 | const devConfigJs = {
7 | mode: 'development',
8 | output: {
9 | filename: 'bound.js',
10 | path: path.resolve(__dirname, 'dist')
11 | },
12 | devtool: 'inline-source-map',
13 | module: {
14 | rules: [
15 | // 处理css/scss/sass
16 | {
17 | test: /\.(sc|sa|c)ss$/,
18 | use: [
19 | {
20 | loader: 'style-loader'
21 | },
22 | {
23 | loader: 'css-loader',
24 | options: {
25 | sourceMap: true
26 | }
27 | },
28 | {
29 | loader: 'postcss-loader',
30 | options: {
31 | ident: 'postcss',
32 | sourceMap: true,
33 | plugins: (loader) => [
34 | require('autoprefixer')({
35 | browsers: [
36 | 'last 10 Chrome versions',
37 | 'last 5 Firefox versions',
38 | 'Safari >= 6',
39 | 'ie > 8'
40 | ]
41 | })
42 | ]
43 | }
44 | },
45 | {
46 | loader: 'sass-loader',
47 | options: {
48 | sourceMap: true
49 | }
50 | }
51 | ]
52 | }
53 | ]
54 | },
55 | plugins: [
56 | // 使用vendor-manifest.json引用dll
57 | new webpack.DllReferencePlugin({
58 | context: __dirname, // context:与Dllplugin里的context所指向的上下文保持一致,这里都是指向了根目录
59 | manifest: require('./vendor-manifest.json')
60 | }),
61 | // 这个主要是将生成的vendor.dll.js文件加上hash值插入到页面中。
62 | new AddAssetHtmlPlugin([{
63 | // dll文件位置
64 | filepath: path.resolve(__dirname, './static/js/vendor.dll.js'),
65 | // dll最终输出的目录
66 | outputPath: './js',
67 | // dll 引用路径
68 | publicPath: './js',
69 | includeSourcemap: false,
70 | hash: true
71 | }]),
72 | new webpack.NamedModulesPlugin(), // 更方便查看patch的依赖
73 | new webpack.HotModuleReplacementPlugin() // HMR
74 | ],
75 | devServer: {
76 | clientLogLevel: 'warning', // 输出日志级别
77 | hot: true, // 启用热更新
78 | contentBase: path.resolve(__dirname, 'dist'), // 告诉服务器从哪里提供内容
79 | publicPath: '/', // 此路径下的打包文件可在浏览器下访问
80 | compress: true, // 启用gzip压缩
81 | host: '0.0.0.0',
82 | port: 9991,
83 | open: true, // 自动打开浏览器
84 | overlay: { // 出现错误或者警告时候是否覆盖页面线上错误信息
85 | warnings: true,
86 | errors: true
87 | },
88 | quiet: false,
89 | proxy: { // 设置代理
90 | '/dev': {
91 | target: 'http://dev.xxxx.com.cn',
92 | changeOrigin: true,
93 | pathRewrite: {
94 | '^/dev': ''
95 | }
96 | /**
97 | * 如果你的配置是
98 | * pathRewrite: {
99 | * '^/dev': '/order/api'
100 | * }
101 | * 即本地请求 /dev/getOrder => 实际上是 http://dev.xxxx.com.cn/order/api/getOrder
102 | */
103 | },
104 | '/test': {
105 | target: 'http://test.xxxx.com.cn',
106 | changeOrigin: true,
107 | pathRewrite: {
108 | '^/test': ''
109 | }
110 | },
111 | '/prod': {
112 | target: 'http://prod.xxxx.com.cn',
113 | changeOrigin: true,
114 | pathRewrite: {
115 | '^/prod': ''
116 | }
117 | }
118 | },
119 | watchOptions: { // 监控文件相关配置
120 | poll: true,
121 | ignored: /node_modules/, // 忽略监控的文件夹, 正则
122 | aggregateTimeout: 300 // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
123 | }
124 | }
125 | }
126 |
127 | module.exports = merge(baseWebpackConfig, devConfigJs)
128 |
--------------------------------------------------------------------------------
/webpack.base.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 | const CleanWebpackplugin = require('clean-webpack-plugin')
4 | const VueLoaderPlugin = require('vue-loader/lib/plugin')
5 |
6 | module.exports = {
7 | entry: './src/index.js',
8 | resolve: {
9 | // 模块扩展名, 引入文件今可能写文件名
10 | extensions: ['.js', '.vue', '.json'],
11 | // 配置别名来映射路径
12 | alias: {
13 | '@': path.resolve(__dirname, 'src/'),
14 | // 对于固定的库, 减少递归解析
15 | 'vue': path.resolve(__dirname, 'node_modules/vue/dist/vue.min.js')
16 | },
17 | // 指明第三方模块的绝对路径, 减少路径查找
18 | modules: [path.resolve(__dirname, 'node_modules')]
19 | },
20 | // 配置外部依赖不会打包到boudle
21 | externals: {
22 | jquery: 'jQuery'
23 | },
24 | module: {
25 | // 忽略对没采用模块化的模块进行递归解析
26 | noParse: [/vue\.min\.js/],
27 | rules: [
28 | // 编译js
29 | {
30 | test: /\.js$/,
31 | exclude: /(node_modules|bower_components)/,
32 | include: path.resolve(__dirname, 'src'), // 缩小命中范围, 减少构建时间
33 | use: [
34 | {
35 | loader: 'babel-loader?cacheDirectory' // 通过cacheDirectory选项开启支持缓存
36 | },
37 | {
38 | loader: 'eslint-loader',
39 | options: {
40 | fix: true
41 | }
42 | }
43 | ]
44 | },
45 | // 处理字体
46 | {
47 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
48 | loader: 'url-loader',
49 | options: {
50 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR
51 | limit: 10000,
52 | name: '[name]-[hash:5].[ext]',
53 | output: 'fonts/'
54 | // publicPath: '', 多用于CDN
55 | }
56 | },
57 | // 处理文件
58 | {
59 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
60 | use: [
61 | // 转base64
62 | {
63 | loader: 'url-loader',
64 | options: {
65 | // 具体配置见插件官网
66 | limit: 10000,
67 | name: '[name]-[hash:5].[ext]',
68 | outputPath: 'img/'// outputPath所设置的路径,是相对于 webpack 的输出目录。
69 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址
70 | }
71 | },
72 | {
73 | loader: 'image-webpack-loader',
74 | options: {
75 | mozjpeg: {
76 | progressive: true,
77 | quality: 65
78 | },
79 | // optipng.enabled: false will disable optipng
80 | optipng: {
81 | enabled: false
82 | },
83 | pngquant: {
84 | quality: '65-90',
85 | speed: 4
86 | },
87 | gifsicle: {
88 | interlaced: false
89 | },
90 | // the webp option will enable WEBP
91 | webp: {
92 | quality: 75
93 | }
94 | }
95 | }
96 | ]
97 | },
98 | {
99 | enforce: 'pre',
100 | test: /\.(js|vue)$/,
101 | loader: 'eslint-loader',
102 | exclude: /node_modules/
103 | },
104 | {
105 | test: /\.vue$/,
106 | use: [
107 | {
108 | loader: 'vue-loader',
109 | options: {
110 | loaders: {
111 | sass: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader']
112 | }
113 | }
114 | }
115 | ]
116 | }
117 | ]
118 | },
119 | plugins: [
120 | new CleanWebpackplugin(['dist']),
121 | // 打包模板
122 | new HtmlWebpackPlugin({
123 | inject: true,
124 | hash: true,
125 | cache: true,
126 | chunksSortMode: 'none',
127 | title: 'Webapck4-demo', // 可以由外面传入
128 | filename: 'index.html', // 默认index.html
129 | template: path.resolve(__dirname, 'index.html'),
130 | minify: {
131 | collapseWhitespace: true,
132 | removeComments: true,
133 | removeRedundantAttributes: true,
134 | removeScriptTypeAttributes: true,
135 | removeStyleLinkTypeAttributes: true
136 | }
137 | }),
138 | new VueLoaderPlugin()
139 | ]
140 | }
141 |
--------------------------------------------------------------------------------
/vendor-manifest.json:
--------------------------------------------------------------------------------
1 | {"name":"vendor_library","content":{"./node_modules/async-validator/es/util.js":{"id":0,"buildMeta":{"exportsType":"namespace","providedExports":["warning","format","isEmptyValue","isEmptyObject","asyncMap","complementError","deepMerge"]}},"./node_modules/async-validator/es/rule/index.js":{"id":1,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/axios/lib/utils.js":{"id":2,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/helpers/typeof.js":{"id":3,"buildMeta":{"providedExports":true}},"./node_modules/vue/dist/vue.runtime.esm.js":{"id":4,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/utils/util.js":{"id":5,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/dom.js":{"id":6,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_global.js":{"id":7,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/global.js":{"id":8,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_has.js":{"id":9,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/helpers/extends.js":{"id":10,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/emitter.js":{"id":11,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_hide.js":{"id":12,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-dp.js":{"id":13,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_descriptors.js":{"id":14,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-iobject.js":{"id":15,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks.js":{"id":16,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/messages.js":{"id":17,"buildMeta":{"exportsType":"namespace","providedExports":["newMessages","messages"]}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_core.js":{"id":18,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_is-object.js":{"id":19,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_fails.js":{"id":20,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/validator/index.js":{"id":21,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/locale/index.js":{"id":22,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_an-object.js":{"id":23,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_property-desc.js":{"id":24,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-keys.js":{"id":25,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_library.js":{"id":26,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_uid.js":{"id":27,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-pie.js":{"id":28,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/rule/required.js":{"id":29,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/type.js":{"id":30,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/axios/lib/defaults.js":{"id":31,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/vue-popper.js":{"id":32,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/merge.js":{"id":33,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/scrollbar-width.js":{"id":34,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/input.js":{"id":35,"buildMeta":{"providedExports":true}},"./node_modules/throttle-debounce/debounce.js":{"id":36,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/resize-event.js":{"id":37,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/focus.js":{"id":38,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_export.js":{"id":39,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-primitive.js":{"id":40,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_defined.js":{"id":41,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-integer.js":{"id":42,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_shared-key.js":{"id":43,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_shared.js":{"id":44,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_enum-bug-keys.js":{"id":45,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gops.js":{"id":46,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iterators.js":{"id":47,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_set-to-string-tag.js":{"id":48,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks-ext.js":{"id":49,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks-define.js":{"id":50,"buildMeta":{"providedExports":true}},"./node_modules/timers-browserify/main.js":{"id":51,"buildMeta":{"providedExports":true}},"./node_modules/process/browser.js":{"id":52,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/bind.js":{"id":53,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/adapters/xhr.js":{"id":54,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/createError.js":{"id":55,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/isCancel.js":{"id":56,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/Cancel.js":{"id":57,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/date.js":{"id":58,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/locale.js":{"id":59,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popup/index.js":{"id":60,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/migrating.js":{"id":61,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/clickoutside.js":{"id":62,"buildMeta":{"providedExports":true}},"./node_modules/throttle-debounce/throttle.js":{"id":63,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/scrollbar.js":{"id":64,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/tag.js":{"id":65,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/scroll-into-view.js":{"id":66,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/shared.js":{"id":67,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_ie8-dom-define.js":{"id":68,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_dom-create.js":{"id":69,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-keys-internal.js":{"id":70,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iobject.js":{"id":71,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_cof.js":{"id":72,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-object.js":{"id":73,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-define.js":{"id":74,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_redefine.js":{"id":75,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-create.js":{"id":76,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopn.js":{"id":77,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/validator/string.js":{"id":78,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/whitespace.js":{"id":79,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/type.js":{"id":80,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/range.js":{"id":81,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/enum.js":{"id":82,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/pattern.js":{"id":83,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/method.js":{"id":84,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/number.js":{"id":85,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/boolean.js":{"id":86,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/regexp.js":{"id":87,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/integer.js":{"id":88,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/float.js":{"id":89,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/array.js":{"id":90,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/object.js":{"id":91,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/enum.js":{"id":92,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/pattern.js":{"id":93,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/date.js":{"id":94,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/required.js":{"id":95,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/vue/dist/vue.min.js":{"id":97,"buildMeta":{"providedExports":true}},"./node_modules/setimmediate/setImmediate.js":{"id":98,"buildMeta":{"providedExports":true}},"./node_modules/lodash/lodash.js":{"id":99,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/module.js":{"id":100,"buildMeta":{"providedExports":true}},"./node_modules/vuex/dist/vuex.esm.js":{"id":101,"buildMeta":{"exportsType":"namespace","providedExports":["default","Store","install","mapState","mapMutations","mapGetters","mapActions","createNamespacedHelpers"]}},"./node_modules/axios/index.js":{"id":102,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/axios.js":{"id":103,"buildMeta":{"providedExports":true}},"./node_modules/is-buffer/index.js":{"id":104,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/Axios.js":{"id":105,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/normalizeHeaderName.js":{"id":106,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/settle.js":{"id":107,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/enhanceError.js":{"id":108,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/buildURL.js":{"id":109,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/parseHeaders.js":{"id":110,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isURLSameOrigin.js":{"id":111,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/btoa.js":{"id":112,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/cookies.js":{"id":113,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/InterceptorManager.js":{"id":114,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/dispatchRequest.js":{"id":115,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/transformData.js":{"id":116,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isAbsoluteURL.js":{"id":117,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/combineURLs.js":{"id":118,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/CancelToken.js":{"id":119,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/spread.js":{"id":120,"buildMeta":{"providedExports":true}},"./node_modules/vue-router/dist/vue-router.esm.js":{"id":121,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/element-ui.common.js":{"id":122,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/date-util.js":{"id":123,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/locale/lang/zh-CN.js":{"id":124,"buildMeta":{"providedExports":true}},"./node_modules/deepmerge/dist/cjs.js":{"id":125,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/locale/format.js":{"id":126,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popup/popup-manager.js":{"id":127,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popper.js":{"id":128,"buildMeta":{"providedExports":true}},"./node_modules/resize-observer-polyfill/dist/ResizeObserver.es.js":{"id":129,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/button.js":{"id":130,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/checkbox.js":{"id":131,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/transitions/collapse-transition.js":{"id":132,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/vdom.js":{"id":133,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/tooltip.js":{"id":134,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/types.js":{"id":135,"buildMeta":{"providedExports":true}},"./node_modules/babel-helper-vue-jsx-merge-props/index.js":{"id":136,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/checkbox-group.js":{"id":137,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/after-leave.js":{"id":138,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/progress.js":{"id":139,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/select.js":{"id":140,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/option.js":{"id":141,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/button-group.js":{"id":142,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/index.js":{"id":143,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/normalizeWheel.js":{"id":144,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/UserAgent_DEPRECATED.js":{"id":145,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/isEventSupported.js":{"id":146,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/ExecutionEnvironment.js":{"id":147,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/aria-dialog.js":{"id":148,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/aria-utils.js":{"id":149,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/index.js":{"id":150,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/babel-runtime/core-js/object/assign.js":{"id":151,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/object/assign.js":{"id":152,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.object.assign.js":{"id":153,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_ctx.js":{"id":154,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_a-function.js":{"id":155,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-assign.js":{"id":156,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_array-includes.js":{"id":157,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-length.js":{"id":158,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-absolute-index.js":{"id":159,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/core-js/symbol/iterator.js":{"id":160,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/symbol/iterator.js":{"id":161,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.string.iterator.js":{"id":162,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_string-at.js":{"id":163,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-create.js":{"id":164,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-dps.js":{"id":165,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_html.js":{"id":166,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gpo.js":{"id":167,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/web.dom.iterable.js":{"id":168,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.array.iterator.js":{"id":169,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_add-to-unscopables.js":{"id":170,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-step.js":{"id":171,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/core-js/symbol.js":{"id":172,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/symbol/index.js":{"id":173,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.symbol.js":{"id":174,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_meta.js":{"id":175,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_enum-keys.js":{"id":176,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_is-array.js":{"id":177,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopn-ext.js":{"id":178,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopd.js":{"id":179,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.object.to-string.js":{"id":180,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es7.symbol.async-iterator.js":{"id":181,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es7.symbol.observable.js":{"id":182,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/input-number.js":{"id":183,"buildMeta":{"providedExports":true}}}}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webpack-demo
2 |
3 | 玩转webpack4。
4 |
5 |
6 | # webpack4常规配置
7 |
8 | 总结Webpack4常见的配置, 一步步非常详细,不断更新。
9 |
10 | ## 准备开发环境
11 |
12 | ```
13 | - 安装node
14 | - 安装webpack
15 | - npm init 初始化项目
16 | ```
17 |
18 | ## 目录结构
19 |
20 |
21 | 
22 |
23 | ## 写跑一个小demo
24 |
25 | ```
26 | // src/index.js
27 |
28 | import _ from 'lodash'
29 |
30 | function create_div_element () {
31 | const div_element = document.createElement('div')
32 | div_element.innerHTML = _.join(['kobe', 'cpul'], ' ')
33 | return div_element
34 | }
35 |
36 | const div_ele = create_div_element()
37 | document.body.appendChild(div_ele)
38 |
39 | ```
40 |
41 | ```
42 | // dist/index.html
43 |
44 |
45 |
46 |
47 |
48 | webpack4
49 |
50 |
51 |
52 |
53 |
54 |
55 | ```
56 |
57 | ```
58 | // webpack.config.js
59 |
60 | const path = require('path')
61 |
62 | module.exports = {
63 | entry: './src/index.js',
64 | mode: 'development',
65 | output: {
66 | filename: 'bound.js',
67 | path: path.resolve(__dirname, 'dist')
68 | }
69 | }
70 |
71 | ```
72 |
73 | 然后通过[npx](https://github.com/zkat/npx#readme)执行webpack进行打包。
74 |
75 | 或者配成一个script命令也可以。
76 |
77 | ```
78 | "scripts": {
79 | "build": "npx webpack -c webpack.config.js"
80 | }
81 | ```
82 |
83 | ```
84 | npx webpack
85 | ```
86 |
87 | 在浏览器打开index.html就会发现代码执行成功了。
88 |
89 |
90 | ## webpack处理CSS
91 |
92 | 假设我们现在需要在index.js引入css文件。
93 |
94 | ```
95 | // index.js
96 |
97 | import './style/reset.css'
98 | ```
99 |
100 |
101 | 我们需要使用专门的loader来解析css, 并把css注入到html文件
102 | ```
103 | npm i -D css-loader style-loader
104 | ```
105 |
106 | 修改webpack配置文件
107 | ```
108 | const path = require('path')
109 |
110 | module.exports = {
111 | entry: './src/index.js',
112 | mode: 'development',
113 | output: {
114 | filename: 'bound.js',
115 | path: path.resolve(__dirname, 'dist')
116 | },
117 | module: {
118 | rules: [
119 | {
120 | test: /\.css/,
121 | use: ['style-loader', 'css-loader'] // use的顺序从右往左
122 | }
123 | ]
124 | }
125 | }
126 | ```
127 | 这个时候你在npx webpack, 打包后执行index.html你会发现css已经注入成功了。
128 |
129 | ## webpack处理sass文件
130 |
131 | 现在前端项目都是使用一些css预处理器来帮助更好的使用CSS,如Sass等。
132 |
133 | 假设我们现在index.js中需要引入一个base.scss文件。
134 | 那么webpack改如何处理sass/scss文件呢?
135 |
136 | ```
137 | npm install sass-loader node-sass -D
138 | ```
139 |
140 | ```
141 | // src/style/base.scss
142 |
143 | $bd-bg: pink;
144 | body {
145 | background: $bd-bg;
146 | }
147 | ```
148 |
149 | ```
150 | // index.js
151 |
152 | import './style/base.scss'
153 | ```
154 |
155 | 更过配置文件处理scss
156 | ```
157 | const path = require('path')
158 |
159 | module.exports = {
160 | entry: './src/index.js',
161 | mode: 'development',
162 | output: {
163 | filename: 'bound.js',
164 | path: path.resolve(__dirname, 'dist')
165 | },
166 | module: {
167 | rules: [
168 | {
169 | test: /\.(sc|sa|c)ss$/,
170 | use: ['style-loader', 'css-loader', 'sass-loader'] // use的顺序从右往左
171 | }
172 | ]
173 | }
174 | }
175 |
176 | ```
177 |
178 | ## webpack为sass添加source map
179 |
180 | 配置source map是为了当出现错误时候方便我们进行定位调试, 当然我们在生产环境不需要启动这个。
181 |
182 | 像我们上面例子中, 你会发现打包后我们看不出scss来自哪个文件。
183 |
184 |
185 | 
186 |
187 | 修改webpack配置文件。
188 |
189 | ```
190 | const path = require('path')
191 |
192 | module.exports = {
193 | entry: './src/index.js',
194 | mode: 'development',
195 | output: {
196 | filename: 'bound.js',
197 | path: path.resolve(__dirname, 'dist')
198 | },
199 | module: {
200 | rules: [
201 | {
202 | test: /\.(sc|sa|c)ss$/,
203 | use: [
204 | {
205 | loader: 'style-loader'
206 | },
207 | {
208 | loader: 'css-loader',
209 | options: {
210 | sourceMap: true
211 | }
212 | },
213 | {
214 | loader: 'sass-loader',
215 | options: {
216 | sourceMap: true
217 | }
218 | }
219 | ]
220 | }
221 | ]
222 | }
223 | }
224 |
225 | ```
226 |
227 | 打包后在浏览器打开index.html.
228 |
229 |
230 | 
231 |
232 | ## webpack为css添加CSS3前缀
233 |
234 | [PostCSS](https://www.postcss.com.cn/)是一个用 JavaScript 工具和插件转换 CSS 代码的工具, 功能强大, 我们最常用的就是利用PostCSS帮我们Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。
235 |
236 |
237 | ```
238 | npm i -D postcss-loader autoprefixer postcss-import
239 |
240 | // postcss-import: 在使用@import css文件时候让webpack可以监听并编译
241 | // postcss-nextcss: 支持css4
242 | ```
243 |
244 | 修改配置文件
245 |
246 | ```
247 | rules: [
248 | {
249 | test: /\.(sc|sa|c)ss$/,
250 | use: [
251 | {
252 | loader: 'style-loader'
253 | },
254 | {
255 | loader: 'css-loader',
256 | options: {
257 | sourceMap: true
258 | }
259 | },
260 | {
261 | loader: 'postcss-loader',
262 | options: {
263 | ident: 'postcss',
264 | sourceMap: true,
265 | plugins: loader => [
266 | // 可以配置多个插件
267 | require('autoprefixer')({
268 | browsers: [' > 0.15% in CN ']
269 | })
270 | ]
271 | }
272 | },
273 | {
274 | loader: 'sass-loader',
275 | options: {
276 | sourceMap: true
277 | }
278 | }
279 | ]
280 | }
281 | ]
282 | ```
283 |
284 |
285 | 
286 |
287 | ## 抽离样式表为单独的css文件并打版本号
288 |
289 | 抽离css前提是我们只在生产环境这么做, 因此你的配置文件的mode: production。
290 |
291 | 另外抽离了css就不能在使用style-loader注入到html文件。
292 |
293 | ```
294 | npm i -D mini-css-extract-plugin
295 | ```
296 |
297 | 配置一个script命名
298 |
299 | ```
300 | "scripts": {
301 | "dist": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js"
302 | },
303 | ```
304 |
305 | 添加一个webpack.prod.config.js.当然正式项目我们是会拆分配置文件, 然后通过merge处理。
306 |
307 | ```
308 | - webpack.base.config.js
309 | - webpack.dev.config.js
310 | - webpack.prod.config.js
311 | - webpack.vue.config.js
312 | ```
313 |
314 | 这里demo就没有这么做, 所以代码有些冗余。
315 |
316 | ```
317 | // webpack.prod.config.js
318 |
319 | const path = require('path')
320 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
321 | const devMode = process.env.NODE_ENV !== 'production'
322 |
323 | module.exports = {
324 | entry: './src/index.js',
325 | mode: 'production',
326 | output: {
327 | filename: 'bound.js',
328 | path: path.resolve(__dirname, 'dist')
329 | },
330 | module: {
331 | rules: [
332 | {
333 | test: /\.(sc|sa|c)ss$/,
334 | use: [
335 | MiniCssExtractPlugin.loader,
336 | {
337 | loader: 'css-loader'
338 | },
339 | {
340 | loader: 'postcss-loader',
341 | options: {
342 | ident: 'postcss',
343 | plugins: (loader) => [
344 | require('autoprefixer')({
345 | browsers: [
346 | 'last 10 Chrome versions',
347 | 'last 5 Firefox versions',
348 | 'Safari >= 6',
349 | 'ie > 8'
350 | ]
351 | })
352 | ]
353 | }
354 | },
355 | {
356 | loader: 'sass-loader'
357 | }
358 | ]
359 | }
360 | ]
361 | },
362 | plugins: [
363 | new MiniCssExtractPlugin({
364 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名
365 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css'
366 | })
367 | ]
368 | }
369 |
370 | ```
371 |
372 | 打包后你会发现
373 |
374 |
375 | 
376 |
377 | 这个时候我们如果去使用只能在index.html去引用它了, 很明显这是不方便的, 因为我们css文件肯定很庞大, 后面会解决这个问题, 这里就略过。
378 |
379 |
380 | ## webpack压缩JS和CSS
381 |
382 | 压缩的作用自然是为了减小包的体积了, 提升加载效率, 因此压缩都是配置在生产环境。
383 |
384 | ### 压缩css
385 |
386 | Webpack后面版本应该会内置CSS压缩, 目前先手工配置。
387 |
388 | ```
389 | npm i -D optimize-css-assets-webpack-plugin
390 | ```
391 |
392 | 更改配置文件:
393 |
394 | ```
395 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin')
396 |
397 | optimization: {
398 | minimizer: [
399 | // 压缩CSS
400 | new OptimizeCSSAssertsPlugin({})
401 | ]
402 | }
403 | ```
404 |
405 |
406 | 
407 |
408 | ### JS压缩
409 |
410 |
411 | ```
412 | npm i -D uglifyjs-webpack-plugin
413 | ```
414 |
415 | 修改配置文件
416 |
417 | ```
418 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
419 |
420 | optimization: {
421 | minimizer: [
422 | // 压缩JS
423 | new UglifyJsPlugin({
424 | // 有很多可以配置
425 | cache: true,
426 | parallel: true,
427 | sourceMap: true,
428 | uglifyOptions: {
429 | // 在UglifyJs删除没有用到的代码时不输出警告
430 | warnings: false,
431 | output: {
432 | // 删除所有的注释
433 | comments: false,
434 | // 最紧凑的输出
435 | beautify: false
436 | },
437 | compress: {
438 | // 删除所有的 `console` 语句
439 | // 还可以兼容ie浏览器
440 | drop_console: true,
441 | // 内嵌定义了但是只用到一次的变量
442 | collapse_vars: true,
443 | // 提取出出现多次但是没有定义成变量去引用的静态值
444 | reduce_vars: true,
445 | }
446 | }
447 | })
448 | ]
449 | }
450 | ```
451 |
452 |
453 |
454 | 这个时候去打包我发现一个错误, [ERROR in js/background.js from UglifyJs Unexpected token: keyword (const)](https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/389)。
455 |
456 | Uglify-js不支持es6语法,请使用terser插件, 于是我们更改使用terser插件试试, 其实你继续用uglifyjs-webpack-plugin也可以, 只需要配合babel先转下。
457 |
458 | ```
459 | npm install terser-webpack-plugin -D
460 | ```
461 |
462 |
463 | 更多使用见官网[terser-webpack-plugin](https://www.npmjs.com/package/terser-webpack-plugin)。
464 |
465 | ```
466 |
467 | optimization: {
468 | minimizer: [
469 | // 压缩JS
470 | new TerserPlugin({
471 | cache: true,
472 | parallel: true,
473 | sourceMap: true,
474 | // 等等详细配置见官网
475 | }),
476 |
477 | ]
478 | }
479 | ```
480 |
481 |
482 | 
483 |
484 | ## webpack处理带哈希值的文件名引入问题
485 |
486 | 我们给打包的文件打上hash是为了解决缓存更新问题,常见需要打上hash的地方有。
487 |
488 | ```
489 | output: {
490 | filename: 'bound.[hash:5].js',
491 | path: path.resolve(__dirname, 'dist')
492 | }
493 | ```
494 |
495 | ```
496 | // 提取CSS
497 | new MiniCssExtractPlugin({
498 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名
499 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css'
500 | })
501 | ```
502 |
503 | 但是打上hash我们怎么引入是一个问题。
504 |
505 | html-webpack-plugin插件可以把js/css注入到一个模板文件, 所以不需要再手动更改引用。
506 |
507 | ```
508 | npm i -D html-webpack-plugin
509 | ```
510 |
511 | 更改配置文件
512 |
513 | ```
514 | const HtmlWebpackPlugin = require('html-webpack-plugin')
515 |
516 | plugins: [
517 | // 打包模板
518 | new HtmlWebpackPlugin({
519 | inject: true,
520 | hash: true,
521 | cache: true,
522 | chunksSortMode: 'none',
523 | title: 'Webapck4-demo', // 可以由外面传入
524 | filename: 'index.html', // 默认index.html
525 | template: path.resolve(__dirname, 'index.html'),
526 | minify: {
527 | collapseWhitespace: true,
528 | removeComments: true,
529 | removeRedundantAttributes: true,
530 | removeScriptTypeAttributes: true,
531 | removeStyleLinkTypeAttributes: true
532 | }
533 | })
534 | ],
535 | ```
536 |
537 | 设置一个模板文件。
538 |
539 | ```
540 | // index.html
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 | <%= htmlWebpackPlugin.options.title %>
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 | ```
559 | 打包后的文件:
560 |
561 | 
562 |
563 | ```
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 | ```
584 |
585 | ## webpack清理打包后的dist目录
586 |
587 | 我们会发现每次打包后dist文件夹都会不断增加文件, 显然这个方面我们需要处理, 但是某些情况下我们不需要去清理, 比如坑爹的微信公众号缓存问题。
588 |
589 | ```
590 | npm i -D clean-webpack-plugin
591 | ```
592 |
593 | 修改配置文件
594 |
595 | ```
596 | const CleanWebpackplugin = require('clean-webpack-plugin')
597 |
598 | plugins: [
599 | // 清理dist目录
600 | new CleanWebpackplugin(['dist'])
601 | ]
602 | ```
603 |
604 | ## webpack处理图片以及优化
605 |
606 | 我们这里只是为了测试, 在index.html模板文件添加一个dom去使用图片。
607 |
608 | ```
609 | // index.html
610 |
611 |
612 | // base.scss
613 | .logo {
614 | background: url('../assets/logo.png') no-repeat;
615 | width: 100px;
616 | height: 100px;
617 | background-size: contain;
618 | }
619 | ```
620 |
621 | 使用file-loader来处理文件的导入
622 |
623 | ```
624 | npm i -D file-loader
625 | ```
626 |
627 | 修改配置文件
628 |
629 | ```
630 | rules: [
631 | // 处理文件
632 | {
633 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
634 | use: [
635 | {
636 | loader: 'file-loader',
637 | options: {
638 | // 具体配置见插件官网
639 | limit: 1,
640 | name: '[name]-[hash:5].[ext]',
641 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。
642 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址
643 | },
644 | },
645 | ]
646 | },
647 | ]
648 | ```
649 |
650 | 下面继续对图片进行优化和压缩
651 |
652 | ```
653 | npm i -D image-webpack-loader
654 | ```
655 |
656 | 修改配置文件
657 |
658 | ```
659 | // 处理文件
660 | {
661 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
662 | use: [
663 | {
664 | loader: 'file-loader',
665 | options: {
666 | // 具体配置见插件官网
667 | limit: 10000,
668 | name: '[name]-[hash:5].[ext]',
669 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。
670 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址
671 | },
672 | },
673 | {
674 | loader: 'image-webpack-loader',
675 | options: {
676 | mozjpeg: {
677 | progressive: true,
678 | quality: 65
679 | },
680 | // optipng.enabled: false will disable optipng
681 | optipng: {
682 | enabled: false,
683 | },
684 | pngquant: {
685 | quality: '65-90',
686 | speed: 4
687 | },
688 | gifsicle: {
689 | interlaced: false,
690 | },
691 | // the webp option will enable WEBP
692 | webp: {
693 | quality: 75
694 | }
695 | }
696 | }
697 | ]
698 | },
699 | ```
700 |
701 | 压缩前图片大小181.46kb.
702 |
703 | 
704 |
705 | 压缩后29kb.
706 |
707 | 
708 |
709 |
710 | ## webpack把图片转为base64以及字体处理
711 |
712 | 通过把一些小的图片转为base65(DataURl)可以减少http请求, 提升访问效率。
713 |
714 | ```
715 | npm i -D url-loader
716 | ```
717 |
718 | 修改配置文件
719 |
720 | ```
721 | // 处理文件
722 | {
723 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
724 | use: [
725 | {
726 | loader: 'url-loader',
727 | options: {
728 | // 具体配置见插件官网
729 | limit: 10000,
730 | name: '[name]-[hash:5].[ext]',
731 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。
732 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址
733 | },
734 | },
735 | {
736 | loader: 'image-webpack-loader',
737 | options: {
738 | mozjpeg: {
739 | progressive: true,
740 | quality: 65
741 | },
742 | // optipng.enabled: false will disable optipng
743 | optipng: {
744 | enabled: false,
745 | },
746 | pngquant: {
747 | quality: '65-90',
748 | speed: 4
749 | },
750 | gifsicle: {
751 | interlaced: false,
752 | },
753 | // the webp option will enable WEBP
754 | webp: {
755 | quality: 75
756 | }
757 | }
758 | }
759 | ]
760 | },
761 | ```
762 | 这里测试的话我们需要准备一个小的图片即可,如上述配置所述只要小于10kb就会用base64替代。
763 |
764 |
765 | .
766 |
767 | 字体处理的话配置如下:
768 |
769 | ```
770 | {
771 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
772 | loader: 'url-loader',
773 | options: {
774 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR
775 | limit: 10000,
776 | name: '[name]-[hash:5].[ext]',
777 | output: 'fonts/',
778 | // publicPath: '', 多用于CDN
779 | }
780 | },
781 | ```
782 |
783 |
784 |
785 | ## webpack配置分层
786 |
787 | 之前有提过webpack根据不同的环境我们会加载不同的配置。我们只需要提取出三部分。
788 |
789 | ```
790 | - base: 公共的部分
791 | - dev: 开发环境部分
792 | - prod: 生产环境部分
793 | ```
794 |
795 | ```
796 | npm i -D webpack-merge
797 | ```
798 |
799 | 我们这里现在简单分层:正式项目最好创建一个config/webpack目录管理。
800 |
801 |
802 | 
803 |
804 | 下面是源代码。
805 |
806 | ```
807 | "scripts": {
808 | "dev": "cross-env NODE_ENV=development npx webpack --progress --config webpack.dev.config.js",
809 | "build": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js"
810 | },
811 | ```
812 |
813 | ```
814 | // webapck.base.config.js
815 |
816 | const path = require('path')
817 | const HtmlWebpackPlugin = require('html-webpack-plugin')
818 | const CleanWebpackplugin = require('clean-webpack-plugin')
819 |
820 | module.exports = {
821 | entry: './src/index.js',
822 | module: {
823 | rules: [
824 | // 处理字体
825 | {
826 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
827 | loader: 'url-loader',
828 | options: {
829 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR
830 | limit: 10000,
831 | name: '[name]-[hash:5].[ext]',
832 | output: 'fonts/',
833 | // publicPath: '', 多用于CDN
834 | }
835 | },
836 | // 处理文件
837 | {
838 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
839 | use: [
840 | // 转base64
841 | {
842 | loader: 'url-loader',
843 | options: {
844 | // 具体配置见插件官网
845 | limit: 10000,
846 | name: '[name]-[hash:5].[ext]',
847 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。
848 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址
849 | },
850 | },
851 | {
852 | loader: 'image-webpack-loader',
853 | options: {
854 | mozjpeg: {
855 | progressive: true,
856 | quality: 65
857 | },
858 | // optipng.enabled: false will disable optipng
859 | optipng: {
860 | enabled: false,
861 | },
862 | pngquant: {
863 | quality: '65-90',
864 | speed: 4
865 | },
866 | gifsicle: {
867 | interlaced: false,
868 | },
869 | // the webp option will enable WEBP
870 | webp: {
871 | quality: 75
872 | }
873 | }
874 | }
875 | ]
876 | }
877 | ]
878 | },
879 | plugins: [
880 | // 打包模板
881 | new HtmlWebpackPlugin({
882 | inject: true,
883 | hash: true,
884 | cache: true,
885 | chunksSortMode: 'none',
886 | title: 'Webapck4-demo', // 可以由外面传入
887 | filename: 'index.html', // 默认index.html
888 | template: path.resolve(__dirname, 'index.html'),
889 | minify: {
890 | collapseWhitespace: true,
891 | removeComments: true,
892 | removeRedundantAttributes: true,
893 | removeScriptTypeAttributes: true,
894 | removeStyleLinkTypeAttributes: true
895 | }
896 | }),
897 | // 清理dist目录
898 | new CleanWebpackplugin(['dist'])
899 | ]
900 | }
901 |
902 | ```
903 |
904 |
905 | ```
906 | // webpack.dev.config.js
907 |
908 | const path = require('path')
909 | const merge = require('webpack-merge')
910 | const baseWebpackConfig = require('./webpack.base.config.js')
911 |
912 |
913 | module.exports = merge(baseWebpackConfig, {
914 | mode: 'development',
915 | output: {
916 | filename: 'bound.js',
917 | path: path.resolve(__dirname, 'dist')
918 | },
919 | module: {
920 | rules: [
921 | // 处理css/scss/sass
922 | {
923 | test: /\.(sc|sa|c)ss$/,
924 | use: [
925 | {
926 | loader: 'style-loader',
927 | },
928 | {
929 | loader: 'css-loader',
930 | options: {
931 | sourceMap: true
932 | }
933 | },
934 | {
935 | loader: 'postcss-loader',
936 | options: {
937 | ident: 'postcss',
938 | sourceMap: true,
939 | plugins: (loader) => [
940 | require('autoprefixer')({
941 | browsers: [
942 | 'last 10 Chrome versions',
943 | 'last 5 Firefox versions',
944 | 'Safari >= 6',
945 | 'ie > 8'
946 | ]
947 | })
948 | ]
949 | }
950 | },
951 | {
952 | loader: 'sass-loader',
953 | options: {
954 | sourceMap: true
955 | }
956 | }
957 | ]
958 | }
959 | ]
960 | }
961 | })
962 |
963 | ```
964 |
965 |
966 | ```
967 | // webapck.prod.config.js
968 |
969 | const path = require('path')
970 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
971 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin')
972 | const TerserPlugin = require('terser-webpack-plugin')
973 | const merge = require('webpack-merge')
974 | const baseWebpackConfig = require('./webpack.base.config.js')
975 | const devMode = process.env.NODE_ENV !== 'production'
976 |
977 | module.exports = merge(baseWebpackConfig, {
978 | mode: 'production',
979 | output: {
980 | filename: 'bound.[hash:5].js',
981 | path: path.resolve(__dirname, 'dist')
982 | },
983 | module: {
984 | rules: [
985 | // 处理css/scss/sass
986 | {
987 | test: /\.(sc|sa|c)ss$/,
988 | use: [
989 | MiniCssExtractPlugin.loader,
990 | {
991 | loader: 'css-loader'
992 | },
993 | {
994 | loader: 'postcss-loader',
995 | options: {
996 | ident: 'postcss',
997 | plugins: (loader) => [
998 | require('autoprefixer')({
999 | browsers: [
1000 | 'last 10 Chrome versions',
1001 | 'last 5 Firefox versions',
1002 | 'Safari >= 6',
1003 | 'ie > 8'
1004 | ]
1005 | })
1006 | ]
1007 | }
1008 | },
1009 | {
1010 | loader: 'sass-loader'
1011 | }
1012 | ]
1013 | }
1014 | ]
1015 | },
1016 | plugins: [
1017 | // 提取CSS
1018 | new MiniCssExtractPlugin({
1019 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名
1020 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css'
1021 | })
1022 | ],
1023 | optimization: {
1024 | minimizer: [
1025 | // 压缩JS
1026 | new TerserPlugin({
1027 | cache: true,
1028 | parallel: true,
1029 | sourceMap: true,
1030 | // 等等详细配置见官网
1031 | }),
1032 | // 压缩CSS
1033 | new OptimizeCSSAssertsPlugin({})
1034 | ]
1035 | }
1036 | })
1037 |
1038 | ```
1039 |
1040 | ## webpack配置js使用sourceMap
1041 |
1042 | 在webpack4使用inline-source-map选项就可以启动错误的堆栈跟踪, 只用于开发环境
1043 |
1044 | ```
1045 | devtool: 'inline-source-map'
1046 | ```
1047 |
1048 | ## 监控文件变化自动编译
1049 |
1050 |
1051 | 简单的方法就是启动watch模式: 如
1052 |
1053 | ```
1054 | "dev": "cross-env NODE_ENV=development npx webpack --progress --config webpack.dev.config.js --watch"
1055 | ```
1056 |
1057 | ## webpack开启热更新和代理配置
1058 |
1059 | 很明显上面watch模式效率不高而且很不方便, 编译完还需要刷新页面, webpack可以开启热更新模式,大大加速开大效率。
1060 |
1061 | ```
1062 | npm i -D webpack-dev-server
1063 | ```
1064 |
1065 | 修改script脚本。
1066 |
1067 | ```
1068 | "dev": "cross-env NODE_ENV=development npx webpack-dev-server --progress --config webpack.dev.config.js"
1069 | ```
1070 |
1071 | 修改配置文件
1072 |
1073 | ```
1074 | const webpack = require('webpack')
1075 |
1076 |
1077 | plugins: [
1078 | new webpack.NamedModulesPlugin(), // 更方便查看patch的依赖
1079 | new webpack.HotModuleReplacementPlugin() // HMR
1080 | ],
1081 | devServer: {
1082 | clientLogLevel: 'warning', // 输出日志级别
1083 | hot: true, // 启用热更新
1084 | contentBase: path.resolve(__dirname, 'dist'), // 告诉服务器从哪里提供内容
1085 | publicPath: '/', // 此路径下的打包文件可在浏览器下访问
1086 | compress: true, // 启用gzip压缩
1087 | // publicPath: './',
1088 | disableHostCheck: true,
1089 | host: '0.0.0.0',
1090 | port: 9999,
1091 | open: true, // 自动打开浏览器
1092 | overlay: { // 出现错误或者警告时候是否覆盖页面线上错误信息
1093 | warnings: true,
1094 | errors: true
1095 | },
1096 | quiet: true,
1097 | proxy: { // 设置代理
1098 | '/dev': {
1099 | target: 'http://dev.xxxx.com.cn',
1100 | changeOrigin: true,
1101 | pathRewrite: {
1102 | '^/dev': ''
1103 | }
1104 | /**
1105 | * 如果你的配置是
1106 | * pathRewrite: {
1107 | '^/dev': '/order/api'
1108 | }
1109 | 即本地请求 /dev/getOrder => 实际上是 http://dev.xxxx.com.cn/order/api/getOrder
1110 | */
1111 | },
1112 | '/test': {
1113 | target: 'http://test.xxxx.com.cn',
1114 | changeOrigin: true,
1115 | pathRewrite: {
1116 | '^/test': ''
1117 | }
1118 | },
1119 | '/prod': {
1120 | target: 'http://prod.xxxx.com.cn',
1121 | changeOrigin: true,
1122 | pathRewrite: {
1123 | '^/prod': ''
1124 | }
1125 | }
1126 | },
1127 | watchOptions: { // 监控文件相关配置
1128 | poll: true,
1129 | ignored: /node_modules/, // 忽略监控的文件夹, 正则
1130 | aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
1131 | }
1132 | }
1133 | ```
1134 |
1135 |
1136 | ## webpack启用babel转码
1137 |
1138 | ```
1139 | npm i -D babel-loader @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime
1140 | ```
1141 |
1142 | 修改配置文件
1143 |
1144 | ```
1145 | // 编译js
1146 | {
1147 | test: /\.js$/,
1148 | exclude: /(node_modules|bower_components)/,
1149 | use: {
1150 | loader: 'babel-loader?cacheDirectory', // 通过cacheDirectory选项开启支持缓存
1151 | options: {
1152 | presets: ['@babel/preset-env']
1153 | }
1154 | }
1155 | },
1156 | ```
1157 |
1158 | 增加.babelrc配置文件
1159 |
1160 |
1161 | ```
1162 | // 仅供参考
1163 |
1164 | {
1165 | "presets": [
1166 | [
1167 | "@babel/preset-env"
1168 | ]
1169 | ],
1170 | "plugins": [
1171 | [
1172 | "@babel/plugin-transform-runtime",
1173 | {
1174 | "corejs": false,
1175 | "helpers": true,
1176 | "regenerator": true,
1177 | "useESModules": false,
1178 | "absoluteRuntime": "@babel/runtime"
1179 | }
1180 | ]
1181 | ]
1182 | }
1183 |
1184 | ```
1185 |
1186 | ## webpack配置eslint校验
1187 |
1188 | ```
1189 | npm i -D eslint eslint-loader
1190 |
1191 | // 校验规则
1192 | npm i -D babel-eslint standard
1193 | ```
1194 |
1195 | 修改webpack配置文件
1196 |
1197 | ```
1198 | // 编译js
1199 | {
1200 | test: /\.js$/,
1201 | exclude: /(node_modules|bower_components)/,
1202 | use: [
1203 | {
1204 | loader: 'babel-loader?cacheDirectory', // 通过cacheDirectory选项开启支持缓存
1205 | options: {
1206 | presets: ['@babel/preset-env']
1207 | }
1208 | },
1209 | {
1210 | loader: 'eslint-loader',
1211 | options: {
1212 | fix: true
1213 | }
1214 | }
1215 | ]
1216 | },
1217 | ```
1218 |
1219 | 增加.eslintrc.js文件
1220 |
1221 | ```
1222 | /*
1223 | * ESLint的JSON文件是允许JavaScript注释的,但在gist里显示效果不好,所以我把.json文件后缀改为了.js
1224 | */
1225 |
1226 | /*
1227 | * ESLint 配置文件优先级:
1228 | * .eslintrc.js(输出一个配置对象)
1229 | * .eslintrc.yaml
1230 | * .eslintrc.yml
1231 | * .eslintrc.json(ESLint的JSON文件允许JavaScript风格的注释)
1232 | * .eslintrc(可以是JSON也可以是YAML)
1233 | * package.json(在package.json里创建一个eslintConfig属性,在那里定义你的配置)
1234 | */
1235 |
1236 | /*
1237 | * 你可以通过在项目根目录创建一个.eslintignore文件告诉ESLint去忽略特定的文件和目录
1238 | * .eslintignore文件是一个纯文本文件,其中的每一行都是一个glob模式表明哪些路径应该忽略检测
1239 | */
1240 |
1241 | module.exports = {
1242 | //ESLint默认使用Espree作为其解析器
1243 | //同时babel-eslint也是用得最多的解析器
1244 | //parser解析代码时的参数
1245 | "parser": "babel-eslint",
1246 | "parserOptions": {
1247 | //指定要使用的ECMAScript版本,默认值5
1248 | "ecmaVersion": 6
1249 | //设置为script(默认)或module(如果你的代码是ECMAScript模块)
1250 | // "sourceType": "script",
1251 | //这是个对象,表示你想使用的额外的语言特性,所有选项默认都是false
1252 | // "ecmafeatures": {
1253 | // //允许在全局作用域下使用return语句
1254 | // "globalReturn": false,
1255 | // //启用全局strict模式(严格模式)
1256 | // "impliedStrict": false,
1257 | // //启用JSX
1258 | // "jsx": false,
1259 | // //启用对实验性的objectRest/spreadProperties的支持
1260 | // "experimentalObjectRestSpread": false
1261 | // }
1262 | },
1263 | //指定环境,每个环境都有自己预定义的全局变量,可以同时指定多个环境,不矛盾
1264 | "env": {
1265 | //效果同配置项ecmaVersion一样
1266 | "es6": true,
1267 | "browser": true,
1268 | "node": true,
1269 | "commonjs": false,
1270 | "mocha": true,
1271 | "jquery": true,
1272 | //如果你想使用来自某个插件的环境时,确保在plugins数组里指定插件名
1273 | //格式为:插件名/环境名称(插件名不带前缀)
1274 | // "react/node": true
1275 | },
1276 |
1277 | //指定环境为我们提供了预置的全局变量
1278 | //对于那些我们自定义的全局变量,可以用globals指定
1279 | //设置每个变量等于true允许变量被重写,或false不允许被重写
1280 | "globals": {
1281 | "globalVar1": true,
1282 | "globalVar2": false
1283 | },
1284 |
1285 | //ESLint支持使用第三方插件
1286 | //在使用插件之前,你必须使用npm安装它
1287 | //全局安装的ESLint只能使用全局安装的插件
1288 | //本地安装的ESLint不仅可以使用本地安装的插件还可以使用全局安装的插件
1289 | //plugin与extend的区别:extend提供的是eslint现有规则的一系列预设
1290 | //而plugin则提供了除预设之外的自定义规则,当你在eslint的规则里找不到合适的的时候
1291 | //就可以借用插件来实现了
1292 | "plugins": [
1293 | // "eslint-plugin-airbnb",
1294 | //插件名称可以省略eslint-plugin-前缀
1295 | // "react"
1296 | ],
1297 |
1298 | //具体规则配置
1299 | //off或0--关闭规则
1300 | //warn或1--开启规则,警告级别(不会导致程序退出)
1301 | //error或2--开启规则,错误级别(当被触发的时候,程序会退出)
1302 | "rules": {
1303 | "eqeqeq": "warn",
1304 | //你也可以使用对应的数字定义规则严重程度
1305 | "curly": 2,
1306 | //如果某条规则有额外的选项,你可以使用数组字面量指定它们
1307 | //选项可以是字符串,也可以是对象
1308 | "quotes": ["error", "double"],
1309 | "one-var": ["error", {
1310 | "var": "always",
1311 | "let": "never",
1312 | "const": "never"
1313 | }],
1314 | //配置插件提供的自定义规则的时候,格式为:不带前缀插件名/规则ID
1315 | // "react/curly": "error"
1316 | },
1317 |
1318 | //ESLint支持在配置文件添加共享设置
1319 | //你可以添加settings对象到配置文件,它将提供给每一个将被执行的规则
1320 | //如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置
1321 | "settings": {
1322 | "sharedData": "Hello"
1323 | },
1324 |
1325 | //Eslint检测配置文件步骤:
1326 | //1.在要检测的文件同一目录里寻找.eslintrc.*和package.json
1327 | //2.紧接着在父级目录里寻找,一直到文件系统的根目录
1328 | //3.如果在前两步发现有root:true的配置,停止在父级目录中寻找.eslintrc
1329 | //4.如果以上步骤都没有找到,则回退到用户主目录~/.eslintrc中自定义的默认配置
1330 | "root": true,
1331 |
1332 | //extends属性值可以是一个字符串或字符串数组
1333 | //数组中每个配置项继承它前面的配置
1334 | //可选的配置项如下
1335 | //1.字符串eslint:recommended,该配置项启用一系列核心规则,这些规则报告一些常见问题,即在(规则页面)中打勾的规则
1336 | //2.一个可以输出配置对象的可共享配置包,如eslint-config-standard
1337 | //可共享配置包是一个导出配置对象的简单的npm包,包名称以eslint-config-开头,使用前要安装
1338 | //extends属性值可以省略包名的前缀eslint-config-
1339 | //3.一个输出配置规则的插件包,如eslint-plugin-react
1340 | //一些插件也可以输出一个或多个命名的配置
1341 | //extends属性值为,plugin:包名/配置名称
1342 | //4.一个指向配置文件的相对路径或绝对路径
1343 | //5.字符串eslint:all,启用当前安装的ESLint中所有的核心规则
1344 | //该配置不推荐在产品中使用,因为它随着ESLint版本进行更改。使用的话,请自己承担风险
1345 | "extends": [
1346 | "standard"
1347 | ]
1348 | }
1349 |
1350 | ```
1351 |
1352 | 增加.eslintignore文件
1353 |
1354 | ```
1355 | /dist/
1356 | /node_modules/
1357 | /*.js
1358 |
1359 | ```
1360 |
1361 |
1362 | ## webpack配置resolve模块解析
1363 |
1364 | 配置alias方便路径的检索效率。
1365 | 配置文件默认扩展名,import时候自动匹配。
1366 |
1367 | ```
1368 | resolve: {
1369 | extensions: ['.js', '.vue', '.json'],
1370 | alias: {
1371 | '@': path.resolve(__dirname, 'src/')
1372 | }
1373 | },
1374 | ```
1375 |
1376 | ## webpack配置外部扩展externals
1377 |
1378 | externals选项可以提供排除打包某些依赖到boundle的方法.例如我们想通过CDN引入jQuery而不是把jQuery打包到boudle。
1379 |
1380 | 这里以jQuery为例:
1381 |
1382 | 修改配置文件
1383 | ```
1384 | // 配置外部依赖不会打包到boudle
1385 | externals: {
1386 | jquery: 'jQuery'
1387 | },
1388 | ```
1389 |
1390 | 在模板文件引入CDN
1391 | ```
1392 | // index.html
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 |
1400 |
1401 |
1402 |
1403 |
1407 | <%= htmlWebpackPlugin.options.title %>
1408 |
1409 |
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 |
1418 | ```
1419 |
1420 | 在index.js使用jquery
1421 |
1422 | ```
1423 | import $ from 'jquery'
1424 |
1425 | // 测试外部扩展配置
1426 | $(function () {
1427 | $('.logo').click(function () {
1428 | console.log('click')
1429 | })
1430 | })
1431 | ```
1432 |
1433 |
1434 |
1435 |
1436 |
1437 | ## webpack打包报表分析以及优化
1438 |
1439 |
1440 | ```
1441 | npm i -D webpack-bundle-analyzer
1442 | ```
1443 |
1444 | 我单独配置了一个命令进行打包分析:
1445 |
1446 |
1447 | ```
1448 | "build:report": "cross-env NODE_ENV=production npx webpack --progress --config webpack.analysis.config.js"
1449 | ```
1450 |
1451 | 当然你其实完全可以通过传参数配置集成到prod那个配置文件如:
1452 |
1453 | ```
1454 | "build:report": "npm run build --report"
1455 | ```
1456 |
1457 | 然后在prod配置环境中如果参数判断:
1458 |
1459 | ```
1460 | // 伪代码
1461 | if (config.build.bundleAnalyzerReport) {
1462 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
1463 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
1464 | }
1465 | ```
1466 |
1467 | 这里给出我的方案
1468 |
1469 | ```
1470 | const merge = require('webpack-merge')
1471 | const prodWebpackConfig = require('./webpack.prod.config.js')
1472 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
1473 |
1474 | module.exports = merge(prodWebpackConfig, {
1475 | plugins: [
1476 | new BundleAnalyzerPlugin() // 打包分析
1477 | ]
1478 | })
1479 |
1480 | ```
1481 |
1482 |
1483 | # 构建优化/打包优化配置
1484 |
1485 |
1486 | # 支持commitlint, eslint hooks
1487 |
--------------------------------------------------------------------------------