前言
少侠,愿你页底归来,已习得webpack精要。
那些你熟悉的概念
那些你熟悉的概念入口起点
- 字符串语法
module.exports = {
entry: './usage/index.js'
}
-
数组语法
向 entry 属性传入「文件路径(file path)数组」将创建多个主入口
module.exports = {
entry: ['./plugins/hello.js','./usage/index.js']
}
问题:数组语法跟主入口文件引入依赖文件有什么区别?
- 对象语法
module.exports = {
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
}
}
输出
const path = require('path');
module.exports = {
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
}
}
说明:
publicPath:声明资源对外链接,如上配置可以通过<script src="/js/usage.index.js">去访问你的js资源。
加载器(Loaders)
loader是对应用程序中的资源进行转换。他们是函数,可以将文件资源作为参数来源,经过一番处理后返回性的文件资源。类似于fis3或者gulp中的插件
- 通过webpack.config.js配置
const path = require('path');
module.exports = {
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
},
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader?modules',
]
}]
}
- 通过require或CLI
require('style-loader!css-loader?modules!./styles.css');
webpack --module-bind jade --module-bind 'css=style!css'
说明:webpack通过「!」区分加载器。
-
加载器是如何解析的?
loader遵循标准的模块解析,多数情况下,从模块路径解析(通常将模块路径认为是npm install 的node_module路径)。
插件
插件是webpack的支柱功能,它的目标在于解决loader无法实现的其他事。webpack插件是一个具有apply 属性的javascript对象。
- 使用配置
const path = require('path');
module.exports = {
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
},
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader?modules',
]
}],
plugins: [
new ManifestPlugin({
fileName: 'manifest.json',
basePath: './build/',
seed: {
name: 'My Manifest'
}
})
]
}
说明:这里使用的是一个缓存插件,具体的作用我们后续讲解。
那些我模糊的名词
那些我模糊的名词模块
-
模块的解析规则
模块的解析原理是将路径转化为最终的绝对路径,然后根据这个路径找到相应模块。
绝对路径:
相对路径:import "/home/me/file"; import "C:\\Users\\me\\file";
说明:在这种情况下,出现 import 或 require 的资源文件的目录被认为是上下文目录。import "../src/file1"; import "./file2";
模块路径:
设置为模块路径时,模块将在 resolve.modules 中指定的所有目录内搜索。也通过 resolve.alias 配置为模块路径创建别名,eg:import "module"; import "module/lib/file";
const path = require('path'); module.exports = { entry: { "usage.index": './usage/index.js', "usage.main": './usage/main.js' }, output: { filename: '[name].js', path: path.resolve(__dirname, 'usage/dist/js'), publicPath: '/js/' }, rules: [{ test: /\.css$/, use: [ 'style-loader', 'css-loader?modules', ] }], plugins: [ new ManifestPlugin({ fileName: 'manifest.json', basePath: './build/', seed: { name: 'My Manifest' } }) ], resolve: { alias: { "jquery": path.resolve(__dirname, 'src/js/libs/jquery-1.11.1.min.js') } } }
依赖图表
从入口点开始,webpack 递归地构建一个依赖图表,这个依赖图表包括你应用所需的每个模块,然后将所有模块打包为少量的包(bundle) - 通常只有一个包 - 可由浏览器加载。
构建目标
因为服务端可浏览器端代码都可以用JavaScript编写,因此,webpack提供了 node 和 web 两种类型的构建,我们可以通过设置构建目标实现不同类型的构建。
- 如何配置?
const path = require('path');
module.exports = {
target: 'node', // 默认是web,可以省略
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
}
// ...
}
通过 module.exports = [clientConfig, serviceConfig],的方式实现多target配置。
-
模块热替换(HMR)
模块热替换功能会在应用程序运行过程替换、添加或删除模块。这使得你可以在独立模块变更后,无需刷新浏览器页面,就可以更新这些模块,极大地提高开发效率。 - 如何配置?
const path = require('path');
const webpack = require('webpack');
module.exports = {
target: 'node', // 默认是web,可以省略
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
},
plugins: [
// 开启全局的模块热替换(HMR)
new webpack.HotModuleReplacementPlugin(),
// 当模块热替换(HMR)时在浏览器控制台输出对用户更友好的模块名字信息
new webpack.NamedModulesPlugin(),
],
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader?modules',
]
}],
// ...
}
你的入口index.js:
import _ from 'lodash';
import styles from './css/main.css';
function component () {
let element = document.createElement('div');
element.innerHTML = _.join(['hello', 'webpack'],' ');
return element;
}
document.body.appendChild(component());
说明:在该配置下,修改 mian.css 样式表后,不需要刷新浏览器页面即可看到更新。
问题:在入口index.js文件中引入样式表,那么最终样式表会跟js文件一起打包,如此会影响到页面的加载性能。那么要如何分离呢?
Answer: 使用 extract-text-webpack-plugin 解决你的问题。
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ...
plugins: [
new ExtractTextPlugin('dist/style.css') // 不支持热替换
],
module: {
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: 'css-loader'
})
}]
}
说明:该插件作用之后,会将入口文件中引用的 main.css 构建成 dist/style.css 。另,该插件不支持热替换,亦即在本地开发过程,使用热替换达到独立模块热替换,在build过程开启开插件做分离构建。
那些我们遇到的问题
那些我们遇到的问题如何做精准的构建
const path = require('path');
module.exports = {
entry: {
"usage.index": './usage/index.js',
"usage.main": './usage/main.js'
},
output: {
filename: '[name].[chunkhash].js', // 每一个文件拥有自己唯一的hash值,不要在开发环境下使用,这会增加编译时间,不能与HMR插件共同使用
path: path.resolve(__dirname, 'usage/dist/js'),
publicPath: '/js/'
}
}
说明:在该配置下,只会对有作更改的入口文件进行构建。区分:filename: '[name].[hash].js',这种配置下,所有的入口文件共享一个hash值,即其中某个入口文件更改之后,会对所有的入口文件做更改。
问题:重复构建导致许多的垃圾文件?文件名每次变更,html引入麻烦?这些....,交给webpack插件,都不是问题!
plugins: [
new ManifestPlugin({
fileName: 'manifest.json',
basePath: './build/',
seed: {
name: 'My Manifest'
}
})
]
使用了插件,会生成一个映射文件,从这里,可以取到对应的文件名:
缓存文件映射表webpack js资源断点调试技巧
-
调试类型:
- source-map:可断点调试,可在生产环境使用
- cheap-module-source-map:可断点调试,可在生产环境使用
- eval-source-map:能看源码,不可断点调试,不可在生产环境使用
这里只是列出部分常用的类型,具体可参考webpack文档。
-
如何配置?
module.exports = {
devtool: 'source-map',// 可断点调试
}
-
开始调试
webpack断点调试 -
如何分离开发和生产环境的配置?
- 设置你的webpack.config.js
function buildConfig(env) { return require('./config/' + env + '.js')({ env: env }) } module.exports = buildConfig(env);
- 设置你的package.json
"build:dev": "webpack --env=dev --progress --profile --colors", "build:dist": "webpack --env=prod --progress --profile --colors",
网友评论