平时我们可以用vue-cli
很方便地搭建Vue
的开发环境,vue-cli
确实是个好东西,让我们不需要关心webpack
等一些繁杂的配置,然后直接开始写业务代码,但这会造成我们过度依赖vue-cli
,忽视了webpack
的重要性,当遇到一些特殊场景时候,例如Vue
多入口的配置、优化项目的打包速度等等时可能会无从下手。
初探webpack
,那么便从搭建简单的webpack
环境开始,首先是初始化并安装依赖。
$ yarn init -y
$ yarn add -D webpack webpack-cli cross-env
首先可以尝试一下webpack
打包程序,webpack
可以零配置进行打包,目录结构如下:
webpack-simple
├── package.json
├── src
│ ├── index.js
│ └── sum.js
└── yarn.lock
// src/sum.js
export const add = (a, b) => a + b;
// src/index.js
import { add } from "./sum";
console.log(add(1, 1));
之后写入一个打包的命令。
// package.json
{
// ...
"scripts": {
"build": "webpack"
},
// ...
}
执行npm run build
,默认会调用node_modules/.bin
下的webpack
命令,内部会调用webpack-cli
解析用户参数进行打包,默认会以src/index.js
作为入口文件。
$ npm run build
执行完成后,会出现警告,这里还提示我们默认mode
为production
,此时可以看到出现了dist
文件夹,此目录为最终打包出的结果,并且内部存在一个main.js
,其中webpack
会进行一些语法分析与优化,可以看到打包完成的结构是。
// src/main.js
(()=>{"use strict";console.log(2)})();
当然我们打包时一般不会采用零配置,此时我们就首先新建一个文件webpack.config.js
。既然webpack
说默认mode
是production
,那就先进行一下配置解决这个问题,因为只是一个简单的webpack
环境我们就不区分webpack.dev.js
和webpack.prod.js
进行配置了,简单的使用process.env.NODE_ENV
在webpack.config.js
中区分一下即可,cross-env
是用以配置环境变量的插件。
// package.json
{
// ...
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
},
// ...
}
const path = require("path");
module.exports = {
mode: process.env.NODE_ENV,
entry: "./src/index.js",
output: {
filename: "index.js",
path:path.resolve(__dirname, "dist")
}
}
我们不光是需要处理js
文件的,还需要处理html
文件,这里就需要使用html-webpack-plugin
插件。
$ yarn add -D html-webpack-plugin
之后在webpack.config.js
中进行配置,简单配置一下相关的输入输出和压缩信息,另外如果要是想每次打包删除dist
文件夹的话可以考虑使用clean-webpack-plugin
插件。
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: process.env.NODE_ENV,
entry: "./src/index.js",
output: {
filename: "index.js",
path:path.resolve(__dirname, "dist")
},
plugins:[
new HtmlWebpackPlugin({
title: "Webpack Template",
filename: "index.html", // 打包出来的文件名 根路径是`module.exports.output.path`
template: path.resolve("./public/index.html"),
hash: true, // 在引用资源的后面增加`hash`戳
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
minifyCSS: true,
minifyJS: true,
},
inject: "body", // `head`、`body`、`true`、`false`
scriptLoading: "blocking" // `blocking`、`defer`
})
]
}
之后新建/public/index.html
文件,输入将要被注入的html
代码。
<%= htmlWebpackPlugin.options.title %>
启动npm run build
,我们就可以在/dist/index.html
文件中看到注入成功的代码了。
Webpack Template
平时开发项目,预览效果时,一般直接访问某个ip
和端口进行调试的,webpack-dev-server
就是用来帮我们实现这个功能,他实际上是基于express
来实现web
服务器的功能,另外webpack-dev-server
打包之后的html
和bundle.js
是放在内存中的,目录里是看不到的,一般会配合webpack
的热更新来使用。
$ yarn add -D webpack-dev-server
接下来要在webpack.config.js
配置devServer
环境,包括package.json
的配置。
// webpack.config.js
// ...
module.exports = {
// ...
devServer: {
hot: true, // 开启热更新
open: true, // 自动打开浏览器预览
compress: true, // 开启gzip
port: 3000 //不指定端口会自动分配
},
// ...
}
// package.json
// ...
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
},
// ...
首先我们可以尝试一下对于.js
中编写的Vue
组件进行构建,即不考虑单文件组件.vue
文件的加载,只是构建一个Vue
对象的实例,为了保持演示的代码尽量完整,此时我们在src
下建立一个main.js
出来作为之后编写代码的主入口,当然我们还需要在index.js
中引入main.js。
首先我们需要安装Vue
,之后才能使用Vue
进行开发。
$ yarn add vue
之后在/src/main.js
中编写如下内容。
// /src/main.js
import Vue from "vue";
new Vue({
el: "#app",
template: "Vue Example"
})
之后我们正式开始处理.vue
文件,首先新建一个App.vue
文件在根目录,此时的目录结构如下所示。
webpack-simple-environment
├── dist
│ ├── index.html
│ └── index.js
├── public
│ └── index.html
├── src
│ ├── App.vue
│ ├── index.js
│ ├── main.js
│ └── sum.js
├── jsconfig.js
├── LICENSE
├── package.json
├── README.md
├── webpack.config.js
└── yarn.lock
之后我们修改一下main.js
以及App.vue
这两个文件。
import Vue from "vue";
import App from "./App.vue";
const app = new Vue({
...App,
});
app.$mount("#app");
{{ msg }}
之后便是使用loader
进行处理的环节了,因为我们此时需要对.vue
文件进行处理,我们需要使用loader
处理他们。
$ yarn add -D vue-loader vue-template-compiler css-loader vue-style-loader
之后需要在webpack.config.js
中编写相关配置,之后我们运行npm run dev
就能够成功运行了。
// webpack.config.js
// ...
const VueLoaderPlugin = require("vue-loader/lib/plugin")
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.css$/,
use: [
"vue-style-loader",
"css-loader"
],
},
],
},
plugins:[
new VueLoaderPlugin(),
// ...
]
}
通常我们需要处理资源文件,同样是需要使用loader
进行处理,主要是对于图片进行处理。
$ yarn add -D url-loader file-loader
// webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: "url-loader",
options: {
esModule: false,
limit: 8192, //小于`8K`,用`url-loader`转成`base64` ,否则使用`file-loader`来处理文件
fallback: {
loader: "file-loader",
options: {
esModule: false,
name: "[name].[hash:8].[ext]",
outputPath: "static", //打包之后文件存放的路径, dist/static
}
},
}
}
]
},
// ...
],
},
// ...
}
{{ msg }}
之后运行npm run dev
,就可以看到效果了,可以在控制台的Element
中看到,小于8K
的图片被直接转成了base64
,而大于8K
的文件被当作了外部资源进行引用了。
使用babel
主要是为了做浏览器的兼容,@babel/core
是babel
核心包,@babel/preset-env
是集成bebal
一些可选方案,可以通过修改特定的参数来使用不同预设,babel-loader
可以使得ES6+
转ES5
,babel
默认只转换语法而不转换新的API
,core-js
可以让不支持ES6+ API
的浏览器支持新API
,当然也可以用babel-polyfill
,相关区别可以查阅一下,建议用core-js。
$ yarn add -D @babel/core @babel/preset-env babel-loader
$ yarn add core-js@3
之后在根目录新建一个babel.config.js
,然后将以下代码写入。
// babel.config.js
module.exports = {
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3,
"modules": false
}
]
]
}
之后修改一下App.vue
,写一个较新的语法?.
。
{{ msg }}
还需要修改一下webpack.config.js
。
// webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
],
},
// ...
}
通常我们一般不只写原生css
,我一般使用sass
这个css
框架,所以此处需要安装sass
以及sass-loader
,sass-loader
请使用低于@11.0.0
的版本,sass-loader@11.0.0
不支持vue@2.6.14
,此外我们通常还需要处理CSS
不同浏览器兼容性,所以此处需要安装postcss-loader
,当然postcss.config.js
也是可以通过postcss.config.js
文件配置一些信息的,比如@/
别名等,另外需要注意,在use
中使用loader
的解析顺序是由下而上的,例如下边的对于scss
文件的解析,是先使用sass-loader
再使用postcss-loader。
yarn add -D sass sass-loader@10.1.1 postcss postcss-loader
之后简单写一个示例,新建文件/src/common/styles.scss
,然后其中写一个变量$color-blue: #4C98F7;
。
$color-blue: #4C98F7;
之后修改App.vue
和webpack.config.js
,然后运行npm run dev
就可以看到Example
这个文字变成了蓝色。
{{ msg }}
// webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.css$/,
use: [
"vue-style-loader",
"css-loader",
"postcss-loader"
],
},
{
test: /\.(scss)$/,
use: [
"vue-style-loader",
"css-loader",
"postcss-loader",
"sass-loader",
]
},
// ...
],
},
// ...
}
$ yarn add -D eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue prettier vue-eslint-parser
根目录下建立.editorconfig
、.eslintrc.js
、.prettierrc.js
,进行一些配置,当然这都是可以自定义的,不过要注意prettier
和eslint
规则冲突的问题。
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
// .prettierrc.js
module.exports = {
"printWidth": 100, // 指定代码长度,超出换行
"tabWidth": 4, // tab 键的宽度
"useTabs": false, // 不使用tab
"semi": true, // 结尾加上分号
"singleQuote": false, // 使用单引号
"quoteProps": "preserve", // 不要求对象字面量属性是否使用引号包裹
"jsxSingleQuote": false, // jsx 语法中使用单引号
"trailingComma": "es5", // 确保对象的最后一个属性后有逗号
"bracketSpacing": true, // 大括号有空格 { name: 'rose' }
"jsxBracketSameLine": false, // 在多行JSX元素的最后一行追加 >
"arrowParens": "avoid", // 箭头函数,单个参数不强制添加括号
"requirePragma": false, // 是否严格按照文件顶部的特殊注释格式化代码
"insertPragma": false, // 是否在格式化的文件顶部插入Pragma标记,以表明该文件被prettier格式化过了
"proseWrap": "preserve", // 按照文件原样折行
"htmlWhitespaceSensitivity": "ignore", // html文件的空格敏感度,控制空格是否影响布局
"endOfLine": "lf" // 结尾是 \n \r \n\r auto
}
我们还可以配置一下lint-staged
,在ESLint
检查有错误自动修复,无法修复则无法执行git add
。
$ yarn add -D lint-staged husky
$ npx husky install
$ npx husky add .husky/pre-commit "npx lint-staged"
// package.json
{
// ...
"lint-staged": {
"*.{js,vue,ts}": [ "eslint --fix" ]
}
}