文章目录
最简单的例子
// main.js
document.write('<h1>Hello World</h1>');
配置文件webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js'
},
output: {
path: __dirname +"/dist/",
filename: 'boundle1.js'
}
};
filename
用于输出文件的文件名。
目标输出目录 path
的绝对路径。
__dirname
是nodejs的语法糖,获取当前路径。
非常明显,entry
这个对象负责写入口文件。格式为属性名:文件相对路径,这里属性名可以在出口文件用到。
webpack
入口文件可以设置多个,但是输出文件只能一个,那输出文件如何做到多个文件命名呢?
多入口文件
有多个文件入口的时候,我们可以这样写:
配置文件webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js',
},
output: {
path: __dirname +"/dist/",
filename: '[name].js'
}
};
输出结果:
明显,[name]
对应的是入口文件的属性名,这样多个入口文件我们只需要设置好属性名就可以找到对应的输出文件了。
除了这个之外,webpack
还提供了其他的语法糖。
模板 | 描述 |
---|---|
[hash] | 模块标识符(module identifier)的 hash |
[chunkhash] | chunk 内容的 hash |
[name] | 模块名称 |
[id] | 模块标识符(module identifier) |
[query] | 模块的 query,例如,文件名 ? 后面的字符串 |
这里官方文档有详细的说明就不在赘叙。
为什么要有这些设置呢?
其实都是为了实现缓存更新,大部分浏览器都是对比缓存,就是会提前检测服务器的缓存是否更新,如果更新则就从服务器下载文件。
一般检测更新都是从修改日期和文件名下手,如果2者发生了变化,就重新修改Last-Modifed
的值,这样客户端重新请求的时候就会知道文件已经修改过了,需要重新下载文件。
Loader
-
让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
-
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
-
loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!
我们从例子来学习
- 处理react的JSX文件
jsx
文件
const React = require('react');
const ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello, world!</h1>,
document.querySelector('#wrapper')
);
配置文件webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'react']
}
}
}
]
}
};
入口文件有jsx
文件,和上面区别在于多了一个module
,里面有一个rules
的数组,里面就是设置了所有的loader
的规则。
test
使用正则去匹配需要处理的文件,找到之后就需要设置使用的处理方式和插件use
,设置了使用babel-loader
能够将JSX/ES6文件转为普通的JS文件,而options
是设置babel-loader
的模式。
- 处理css文件
app.css
body {
background-color: blue;
}
main.js
require('./app.css');
配置文件webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
]
}
};
一开始看到这里会觉得太麻烦了,首先需要一个入口文件导入css文件,然后到处一个js文件,最后在html引入一个js文件,为什么需要处理css呢?我直接引入一个css文件不更方便吗?
如果只是开发一个简单的项目当然不使用webpack来开发当然更加方便。
当时一个项目很复杂的话,并且后面如果需要兼顾可维护性和拓展性,很多时候有些css会写进js文件的,这也是sass和less的出现。
当然webpack也可以将这些一起打包,这里就不赘叙。
- 图片写进js
main.js
var img1 = document.createElement("img");
img1.src = require("./small.png");
document.body.appendChild(img1);
var img2 = document.createElement("img");
img2.src = require("./big.png");
document.body.appendChild(img2);
配置文件webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules:[
{
test: /\.(png|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
};
·limit·
是设置loader的,这里的意思是如果图片大小超过8192kb,则会变成url,不会解析成js
文件。
这里的意义可以将一些小图片合并到一个js文件,这样请求的时候就可以减少http次数,提高渲染的效率,不过需要考虑到如果js文件太大就会增加白屏时间。
插件PLUGINS
插件目的在于解决 loader 无法实现的其他事。比如压缩JS文件、新建html文件、设置打开网页等
- 压缩JS
配置文件webpack.config.js
var webpack = require('webpack');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new UglifyJsPlugin()
]
};
引入模块uglifyjs-webpack-plugin
,在plugins直接配置即可。
- 写入html文件
html-webpack-plugin
能为你创建index.html
,open-browser-webpack-plugin
能够在Webpack
加载时打开一个新的浏览器标签(tab)
main.js
document.write('<h1>Hello World</h1>');
配置文件webpack.config.js
var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlwebpackPlugin({
title: 'Webpack-demos',
filename: 'index.html'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080'
})
]
};
环境变量
我们开发的时候,可以设置一些环境变量。
main.js
document.write('<h1>Hello World</h1>');
if (__DEV__) {
document.write(new Date());
}
配置文件
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
process.env.DEBUG
这个从何而来?
其实是我们打包的时候可以设置的值,cross-env DEBUG=true
webpack-dev-server --open,这样DEBUG就可以传入的给js文件。方便我们做测试模仿用户输入的时候使用。
打包的时候,就会自动将对应的变量换为我们设置的值。
常用loader
-
CSS文件处理相关
style-loader 和 css-loader
使 Webpack 可以处理 import 进来的 css 文件。-
CSS 预处理器: sass-loader和node-sass
-
postcss-loader postcss-preset-env
自动添加css前缀。比如在 Chrome 浏览器下使用尚未标准化的新属性需要添加 -webkit 前缀,在火狐浏览器下需要添加 -moz 前缀,在 IE 浏览器下需要添加 -ms 前缀。如果为每个属性都添加一遍的话,非常繁琐,效率很低。postcss 通过插件系统,提供了很多强大的功能,比如可以通过 autoprefixer 自动添加浏览器前缀,通过 postcss-preset-env 插件,我们可以使用尚未支持的 CSS 特性,插件会帮助我们将未来的 CSS 特性转换成现有的 CSS,类似于 Babel 将 ES6+ 的语法转换成 ES5 的语法。
-
-
VUE相关
vue-loader 会解析 .vue 文件,提取每个语言块,如有必要会通过其它 loader 处理,最后将他们组装成一个 ES Module,它默认导出一个 Vue.js 组件选项的对象。
vue-template-compiler 会接解析 template 标签中的内容,预处理为 JS 渲染函数,并最终注入到从 <script> 导出的组件中。 -
处理资源路径
url-loader 和 file-loader
我们如果在文件中使用图片、字体、视频等文件资源的时候,需要配置对应的 loader 来解析,这里我们用到的 loader 是
常用plugins
-
html-webpack-plugin
用于自动修改html中的js文件引用自动修改问题。 -
VueLoaderPlugin
将其它规则复制并应用到 .vue 文件里相应语言的块中。例如,如果我们有一条匹配 /.js$/ 的规则,那么它会应用到 .vue 文件里的 <script>块。
安装babel
Babel 是一个转码器,负责将 ES2015+ 代码转为 ES5 代码。有了转码器我们可以在开发中使用 ES2015+ 代码,无需担心浏览器是否已经实现。
npm install --save-dev @babel/core @babel/preset-env babel-loader
npm install --save @babel/polyfill
Babel6.0 以后拆分了几个独立的包,并以插件的机制来构筑。@babel/core 是 Babel 的核心功能包,必须安装。
@babel/preset-env 能根据当前的运行环境,自动确定需要的 plugins 和 polyfills。主要负责将代码转成 ES5 语法规则。
babel-polyfill。Babel 编译时只编译语法,并不会编译 API 和实例方法,如:async/await、Promise 等,babel-polyfill 会把这些没有的 API 全部挂载到全局对象,也就是所谓的“垫片”。
babel-loader 是 Webpack 用来转译 JS 代码的加载器。
更新 webpack.config.js:
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
// corejs的值需与安装的core-js的版本一致
// 可以通过 npm ls core-js 查看core-js的版本号
"corejs": "3.6.0",
"useBuiltIns": "usage"
}
]
]
}
本地开发环境
自动构建
执行webpack后面加入--watch
就可以自动监视文件,自动构建。
浏览器自动刷新
首先安装 webpack-dev-server
在 Webpack 的配置文件中配置:
module.exports = {
// ...省略其他配置
devServer: {
contentBase: path.join(__dirname, "dist")
}
}
在 package.json 中定义 npm script。具体如下:
"scripts": {
"start": "webpack-dev-server --open"
},
热模块
devServer: {
contentBase: path.join(__dirname, "dist"),
hot:true
}
sourceMap
定位错误在源文件
sourceMap 有多种选项,开发者可以根据不同的需求进行选择。我们来看一下几种不同的选项之间的对比:
devtool | 构建速度 | 重新构建速度 | 适用环境 | 精准度 |
---|---|---|---|---|
none | +++ | +++ | 生产环境 | 不生成 source map |
source-map | -- | -- | 生产环境 | 映射到原始源代码,source map 作为单独的文件保存 |
inline-source-map | -- | -- | 开发环境 | 映射到原始源代码,source map 转换为 DataUrl 后添加到 bundle 中,会导致文件大小剧增。 |
eval | +++ | +++ | 开发环境 | 映射到转换后的代码,而不是源代码,行数映射不正确 |
eval-source-map | -- | + | 开发环境 | 映射到原始源代码,只映射到行。 |
根据以上信息我们可以得出基本的结论:
生产环境中不生成 sourcemap,或者如果需要拥有错误上报工具,选择 source-map。
开发环境中,根据对代码映射精确度的要求,可以选择 eval, eval-source-map,inline-source-map。
搭建本地 Mock 服务
前后端分离的协作模式下,前后端之间的数据传输都是基于 HTTP 接口实现。显然前端的开发是依赖后端接口实现的。在讲求快速迭代的互联网公司这种串行的等待肯定是不会出现的。前后端约定好接口的出参和入参之后,前端数据模拟(mock)就很有必要了。前端基于接口文档自己模拟一份假数据作为前端的数据源。后端按照约定的文档实现接口功能。待接口完全实现之后,前后端切换到真实的接口进行联调。显然,mock 对于开发效率的提升是非常重要的。
代码检查
-
ESLint
npm install -D eslint eslint-loader eslint-plugin-vue babel-eslint eslint-friendly-formatter
eslint-loader 负责对 JS 的源代码执行静态检查。
用 Vue 官方开发的 ESLint 插件 eslint-plugin-vue 对 .vue 文件进行检查。
使用 eslint-friendly-formatter 来美化代码检查报告
需要配置eslint文件.eslintrc.js
module.exports = { env: { browser: true, node: true, es6: true }, extends: ["eslint:recommended", "plugin:vue/essential"], parserOptions: { parser: "babel-eslint", sourceType: "module" }, plugins: ["vue"], rules: {} };
-
CSS检查器
npm install -D stylelint stylelint-webpack-plugin
首先将 stylelint-webpack-plugin 引入,然后将其添加到 plugins 中。StyleLintPlugin 接收一个配置对象作为参数,files 属性使用 glob 规则匹配 stylelint 需要进行检查的文件。这里我们检查 src 目录下的所有以 Vue、CSS、SCSS、Sass 结尾的文件。
const StyleLintPlugin = require("stylelint-webpack-plugin"); module.exports = { // ...省略其他配置 plugins: [ new StyleLintPlugin({ files: ["src/**/*.{vue,css,scss,sass}"] }) ] };
与 ESLint 一样,stylelint 所有的规则默认都是关闭的。我们需要在 stylelint 的配置文件中启用希望使用的规则。
在根目录创建.stylelintrc.js 文件,添加如下内容:
module.exports = { rules: { "color-no-invalid-hex": true, "color-hex-case": "lower", "unit-whitelist": ["em", "rem", "%", "s", "px"] } };
我们定义了 3 条规则。不允许使用非法的十六进制颜色值,不允许颜色值大写,允许使用的度量单位是 em、rem、%、s、px。