开篇
webpack 在如今的前端开发中, 算是不可绕过的一个工具吧. 特别是在开发 SPA 应用的时候, 无论是开发环境, 还是打包上线, 都十分依赖 webpack.
开发环境
- win10
- node -v: 10.15.0
- NPM -v: 6.4.1
Let's go.
体验 0 配置
进入到工作目录, 然后创建项目
mkdir spa-webpack-demo
初始化
NPM init -y
先来体验下 webpack4 的 0 配置:
安装 webpack
NPM i -D webpack
安装好 webpack 依赖后, 创建 src 文件夹, 并在 src 中新建一个 index.JS.
- mkdir src
- cd src
- type nul> index.JS
修改 package.JSON, 在 scripts 选项中, 添加两个命令:
- "dev": "webpack --mode=development",
- "prod": "webpack --mode=production"
好, 完事了. 接下来跑命令行, 测试一下
NPM run dev
正常情况下, 控制台会有一段如下提示, 因为 webpack 命令需要依赖 webpack-cli, 我们安装即可
Do you want to install 'webpack-cli' (yes/no): yes
webpack-cli 安装完成之后, 会自动继续跑我们的 NPM run dev 指令, 即可看到项目中多了一个 dist 目录, 而且多了一个 main.JS.
接下来继续尝试 NPM run prod, 可以看到 dist/main.JS 已被压缩.
这就是 webpack 号称的零配置, 主要的工作就是定义了默认的 entry 路径 src/index.JS, 定义了默认的 output 路径 dist/main.JS, 然后加了一个 mode 参数, 根据 mode 参数的不同帮我们添加一些预置的打包规则.
循序渐进
上述的流程里, 只是体验了一把零配置的感觉, 连 html 文件都没有, 这里开始加上.
加个 index.HTML
在根目录新建 index.HTML, 随便编写点内容
type nul> index.HTML
说到处理 HTML 文件, 肯定少不了 HTML-webpack-plugin, 安装它
NPM i -D HTML-webpack-plugin
然后再项目根目录新建一个 webpack.config.JS,webpack 会自动使用它
type nul> webpack.config.JS
webpack.config.JS 的内容如下
- const HtmlWebpackPlugin = require('html-webpack-plugin');
- module.exports = {
- module: {
- rules: []
- },
- plugins: [
- new HtmlWebpackPlugin({
- filename: 'index.html',
- template: 'index.html'
- })
- ]
- }
执行 NPM run dev, 即可看到 dist 文件夹多了个 index.HTML, 这个 index.HTML 自动引入了打包后的 dist/main.JS.
加个本地服务器
index.HTML 是生成了, 可总不能每次手动打开它在浏览器里面预览吧, webpack 官方推荐我们用 webpack-dev-server 做服务器, 安装它
NPM i -D webpack-dev-server
安装成功后, 修改 webpack.config.JS, 添加 devServer 选项 和 webpack.HotModuleReplacementPlugin 插件.
对于文件中已经添加过的内容, 后面我都会用注释表示.
- const path = require('path');
- const webpack = require('webpack');
- function resolve(dir) {
- return path.join(__dirname, './', dir)
- }
- module.exports = {
- // module - 略
- devServer: {
- contentBase: resolve('dist'), // 根目录
- hot: true, // 是否开启热替换, 无须手动刷新浏览器
- port: 8081, // 端口
- open: true, // 是否自动打开浏览器
- noInfo: true // 不提示打包信息, 错误和警告仍然会显示
- },
- plugins: [
- new webpack.HotModuleReplacementPlugin(),
- // HtmlWebpackPlugin - 略
- ]
- }
然后修改 package.JSON scripts 的 dev 选项
"dev": "webpack-dev-server --mode=development",
注意: 当 devServer 的 hot 参数为 true 时, 记得要在插件里添加 new webpack.HotModuleReplacementPlugin(), 或者你可以在命令行中带上 hot 参数, 这样就不需要自己再往 plugins 中添加插件了.
"dev": "webpack-dev-server --hot --mode=development"
然后 NPM run dev, 就可以尝试静态资源热替换功能了.
处理 JS, CSS 等其他静态资源
首先我们要清楚一点, webpack 它本身是不知道应该如何处理静态资源的, 但是它提供了 loader 和 plugin 机制.
loader 的作用, 顾名思义: 加载器, 就是匹配到的静态资源, 都要经过 loader 的内部处理, 再返回处理之后的结果. 我觉得, loader 像是一个拦截器.
说到 JS, 我们会想到 babel-loader,babel-loader 是干吗的? 常规操作是, 将匹配到的 JS 文件的 ES6 代码 根据 babelrc 文件内的配置 编译成对应的 ES5 代码.
我们这里先添加一个. babelrc 文件
新增. babelrc 文件
type nul> .babelrc
编辑 .babelrc 内容
- {
- // 预设置, 告诉 Babel 要转换的源码使用了哪些新的语法特性
- // targets, useBuiltIns 等选项用于编译出兼容目标环境的代码
- // 其中 useBuiltIns 如果设为 "usage"
- // Babel 会根据实际代码中使用的 ES6/ES7 代码, 以及与你指定的 targets, 按需引入对应的 polyfill
- // 而无需在代码中直接引入 import '@babel/polyfill', 避免输出的包过大, 同时又可以放心使用各种新语法特性.
- "presets": [
- ["@babel/preset-env", {
- // modules 是否 将 ES6 的 import/export 模块化 转为 babel 的 CommonJs 规范模块化
- "modules": false,
- "targets": {
- // "> 1%" : 支持市场份额超过 1% 的浏览器,
- // ""last 2 versions"": 支持每个浏览器最后两个版本
- // "not ie <= 8": 在之前条件的浏览器中, 排除 IE8 以下的浏览器
- "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
- },
- "useBuiltIns": "usage"
- }]
- ],
- // 所用插件
- // transform-runtime 插件表示不管浏览器是否支持 ES6, 只要是 ES6 的语法, 它都会进行转码成 ES5
- // 这个是需要优化的
- "plugins": ["@babel/plugin-transform-runtime"]
- }
安装 babel 依赖, 注意:
babel 7+ 已经废弃了 presets 中 stage-x 的用法, 改为在 plugins 中添加. 并且应用了 NPM scope 包, 代码全部在 @babel 中, 避免以前那种 babel-preset-xxx, babel-plugin-xxx 的用法
最新的 babel-loader 版本是 8+, 需要依赖 babel-core 版本 7+, 包名为 @babel/core, 版本 6 + 的包名为 babel-core.
再分析上面的 .babelrc 文件, 它用到了 @babel/preset-env, @babel/plugin-transform-runtime, 这些依赖都要我们安装好
如果使用了 @babel/preset-env, 则不支持在 plugins 中 添加 stage-x
NPM i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
谈到 CSS, 我们就应该 想到 style-loader 和 CSS-loader. 先安装它们
NPM i -D style-loader CSS-loader
再安装 url-loader 用于解析静态资源, 如图片, 字体等
NPM i -D url-loader
然后修改 webpack.config.JS 的 rules, 添加如下代码
- module.exports = {
- module: {
- rules: [
- {
- test: /\.JS$/,
- loader: 'babel-loader',
- include: [
- resolve('src'),
- resolve('node_modules/webpack-dev-server/client')
- ]
- },
- {
- test: /\.CSS$/,
- use: ['style-loader', 'css-loader']
- },
- {
- test: /\.(PNG|jpe?g|gif|svg)(\?.*)?$/,
- loader: 'url-loader',
- exclude: [],
- options: {
- limit: 10000,
- name: 'img/[name].[hash:7].[ext]'
- }
- }
- ]
- },
- // devServer - 略
- // plugins - 略
- }
接下来准备开发了, 用 vue 吧.
vue 就不用装在 devDependencies 中了.
- NPM i -S vue
- // vue-loader 依赖 vue-template-compiler 和 vue-style-loader
- NPM i -D vue-loader vue-template-compiler vue-style-loader
修改 webpack.config.JS, 添加 如下代码
- const { VueLoaderPlugin } = require('vue-loader');
- module.exports = {
- resolve: {
- extensions: ['.js', '.vue', '.json'],
- alias: {
- '@': resolve('src')
- }
- },
- module: {
- rules: [
- {
- test: /\.vue$/,
- loader: 'vue-loader'
- },
- // 其他 - 略
- ]
- },
- // devServer - 略
- plugins: [
- // 加在最前面
- new VueLoaderPlugin()
- // 其他 - 略
- ]
- }
src 下 新建一个 views 目录 和 assets 目录,
我在 assets 目录下, 增加了一个 logo.PNG 文件
views 下创建一个 myTest 组件, myTest/index.vue, 编辑 index.vue
- <template>
- <div>
- <i class="logo"></i>
- </div>
- </template>
- <script>
- export default {
- name: 'myTest'
- }
- </script>
- <style scoped>
- .logo {
- display: block;
- margin: auto;
- width: 400px;
- height: 400px;
- background: url(../../assets/logo.PNG);
- }
- </style>
src 目录下新建一个 App.vue, 内容如下
- <template>
- <div id="app">
- <my-test></my-test>
- </div>
- </template>
- <script>
- import myTest from "./views/myTest/index";
- export default {
- name: "App",
- components: {
- myTest
- }
- };
- </script>
编辑 src 目录下的 index.JS, 内容如下:
- import Vue from 'vue';
- import App from './App';
- new Vue({
- el: '#app',
- render: h => h(App)
- })
最后 NPM run dev, 查看效果.
锦上添花
添加 打包进度条 信息, 如下
NPM i -D progress-bar-webpack-plugin
修改 webpack.config.JS
- const ProgressBarPlugin = require('progress-bar-webpack-plugin');
- // ....
- plugins: [
- // 其他 - 略
- new ProgressBarPlugin()
- ]
添加 打包结果消息通知
NPM i -D webpack-build-notifier
修改 webpack.config.JS
- const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
- // ....
- plugins: [
- // 其他 - 略
- new WebpackBuildNotifierPlugin()
- ]
归类打包信息
NPM i -D webpack-dashboard
修改 webpack.config.JS
- const DashboardPlugin = require('webpack-dashboard/plugin');
- // ....
- plugins: [
- // 其他 - 略
- new DashboardPlugin()
- ]
修改 package.JSON
"dev": "webpack-dashboard -- webpack-dev-server --mode=development"
这个我使用了, 感觉效果不是很理想啊, 会新开一个窗口, 而且还不能滚动查看信息, 不清楚是不是哪里用错了.
效果如图:
整个代码结构如图:
尚未完成的功能
production 环境, 需要使用
mini-CSS-extract-plugin
和
optimize-CSS-assets-webpack-plugin
插件, 抽离并优化 CSS 文件
production 环境, 需要使用 UglifyJsPlugin 插件, 压缩 JS 文件, 这个插件允许多核编译
production 环境, 需要使用 optimization 选项 splitChunks
vue-router 与 vuex 的引入
等等
写在最后
希望本文的流程能帮助到有需要的读者, 另外本文的打包功能实现的比较粗糙, 打包速度比较慢, 如果看官有啥建议, 请在评论下告知下我.
如果有错误的地方, 还请指出. 谢谢阅读.
代码地址 https://github.com/jkcaptain/spa-webpack-demo
参考
babel 官方文档 https://babeljs.io/docs/en/babel-preset-env
Webpack4 +babel7 多入口配置详细教程
来源: https://juejin.im/post/5c2ad8a3f265da611d66c186