前端自动化
- 您需要跟踪文件应该加载的正确顺序,包括哪些文件依赖于哪些其他文件,并确保不包含您不需要的任何文件。
- 过多的
会增多网络需求
- 这些都可以自动化
为什么要使用webpack
- 新工具具有很多新特性,避免了一些前辈的问题
- 使用简单,如果仅用来打包js文件不使其他花哨的东西,甚至不用过多的配置
- 插件系统完善,你可以作为工具使用它
开始使用
npm init
-
npm install webpack -D
/ -D 意味这作为依赖安装,相当于 --save-dev -
npm install lodash -S
/ -S 意味着--save - main.js
var map=require("lodash/map");
var square=function(n){
return n*n
}
console.log(map([1,2,3,4,5,6], square));
node main.js
如何使用webpack来打包呢?下面循序渐进的为大家介绍
1. 使用webpack命令行
开始使用webpack而不浪费时间在配置文件上的最简单的方法就是从命令行运行它。 无需使用配置文件的webpack命令的最简单版本就是输入文件路径和输出文件路径。 Webpack将从该输入文件中读取,跟踪其依赖关系树,将所有文件合并到一个文件中,并将文件输出到您指定为输出路径的位置。 对于这个例子,我们的输入路径是main.js,我们要将捆绑的文件输出到bundle.js。 所以,让我们创建一个npm脚本来做到这一点(我们没有在全球安装webpack,所以我们不能直接从命令行运行它)。 在package.json中,编辑部分,如下所示:
"scripts": {
"build": "webpack src/main.js dist/bundle.js",
}
在探索webpack之前,我们再来一遍,通过在重建之前删除dist目录及其内容,并添加一些脚本来执行我们的bundle,让我们的构建脚本变得更加专业。 我们需要做的第一件事是安装del-cli,以便我们可以删除目录,而不会使不使用与我们相同的操作系统的人员(不要因为使用Windows而讨厌我); npm安装del-cli -D应该做的伎俩。 然后,我们将将npm脚本更新为以下内容:
scripts": {
"prebuild": "del-cli dist -f",
"build": "webpack src/main.js dist/bundle.js",
"execute": "node dist/bundle.js",
"start": "npm run build -s && npm run execute -s"
}
我们保持build
与以前一样,但现在我们有prebuild
来做一些清理,这将在build
之前运行,每次build
被告知运行。 我们还有execute
,它使用Node.js来执行捆绑的脚本,我们可以使用start
使用一个命令来完成所有操作(-s位只是使npm脚本不输出为 无用的东西到控制台)。 继续运行npm开始。 您应该看到webpack的输出,快速跟随我们的平方阵列,显示在您的控制台。 恭喜! 刚刚完成了我之前提到的存储库的example1分支中的所有内容。
2. 使用配置的webpack
与使用webpack命令行一样有趣,一旦你开始使用更多的webpack的功能,你将要远离传递所有的选项通过命令行,而是使用一个配置文件 ,这将具有更多的能力,但由于它是用JavaScript编写的,它们也将更加易读。
所以,我们来创建一个配置文件。 在项目的根目录中创建名为webpack.config.js的新文件。 这是默认情况下webpack将要查找的文件名,但如果要将配置文件命名为其他目录或将其放在不同的目录中,则可以将--config [filename]选项传递给webpack。
对于本教程,我们将使用标准文件名,现在我们将尝试使其工作方式与使用命令行一样。 为此,我们需要将以下代码添加到配置文件中:
module.exports = {
entry: './src/main.js',
output: {
path: './dist',
filename: 'bundle.js'
}
};
3. 学习使用loader
使用webpack处理文件有两种方式,loader或者插件,先讨论loader,loader被用来转换或者操作文件指定类型。我们可以对一个文件使用多个loader,例如我们可以先对一个文件进行Eslint操作之后用Babel把他转化为ES5,如果ESLint给出警告,竟会打印出警告信息,如果报错,就不会继续运行后面的loader。
下面用ES2015语法来下写main2.js
import { map } from 'lodash';
console.log(map([1,2,3,4,5,6], n => n*n));
上面代码运用ES2015写法,用了import 代替 require (import {map} from loadsh
这样写会把整个loadsh引入,当然也可以用 import map from 'lodash/map
) ;用箭头函数代替了function。这些都是ES2015语法
我们需要编译使之在一些老旧浏览器中运行良好,为此我们需要使用Babel和webpack来运行编译。
需要的loader:
- babel-core(Babel 核心功能,完成大部分工作)
- babel-loader(webpack提供的和babel-core的接口)
- babel-preset-es2015(告诉babeles2015编译为ES5的规则)
- babel-plugin-transform-runtime , babel-polyfill(这两者都改变了Babel向您的代码库添加polyfills和helper函数的方式,尽管它们有所不同,因此它们适合于不同类型的项目。)
安装这些包,然后更新webpack.config.js代码
module.exports = {
entry: './src/main.js',
output: {
path: './dist',
filename: 'bundle.js'
},
module: {
rules: [
…
]
}
};
我们看到增加了module,module下两个有一个rules属性,这是一个数组,对于每组rule,我们设置两个选项test
和loader
.
- test用来测试文件类型,通常是一个正则表达式,例如
/\.js$/
就是以js结尾的文件名,/\.jsx?$/
匹配以js或jsx结尾的文件. -
loader
用来指定运用哪些loader处理匹配到的文件。通过传入带有加载器名称的字符串来指定,用!
分隔。例如babel-loader!eslint-loader
,webpack从右向做开始编译,先经过eslint再经过babel-loader,如果loader有特殊选项你想指定,你可以使用babel-loader?fakeoption=true!eslint-loader
这种形式,用option
形式。
module:{
rules:[
{
test:/\.jsx?$/,
loader:'babel-core',
exclude: /node_modules/,
option:{
plugins: ['transform-runtime'],
presets: ['es2015']
}
}
]
}
我们需要设置预设,使所有的ES2015功能都将转换为ES5,我们还将设置使用我们安装的转换运行时插件。如前所述,这个插件是没有必要的,但它在那里告诉你如何做到这一点。另一种方法是使用.babelrc文件来设置这些选项,但是我无法向您展示如何在webpack中执行此操作。一般来说,我建议使用.babelrc,但我们将在此项目中保留配置。exclude文件排除了node_modules文件夹.
4. 使用 Handlebars loader
使用handlebars模板的loader,仅仅举个例子
- 安装handlebars以及他的loader
cnpm install handlebars handlebars-loader -D
-
import template from 'handlebars-loader!./numberlist.hbs
或者使用loader{ test: /\.hbs$/, loader: 'handlebars-loader' }
. - 使用npm start就会打印出li标签
5. 学习使用插件
除了loader之外,插件是将自定义功能安装到Webpack中。您可以自由地将它们添加到Webpack工作流程中,因为它们不仅限于在加载特定文件类型时被使用;它们几乎可以在任何地方注入,因此能够做得更多.下面举两个关于插件的例子.
- HTML Webpack Plugin
- npm i http-server html-webpack-plugin -D
- package.json
"scripts": {
"prebuild": "del-cli dist -f",
"build": "webpack",
"server": "http-server ./dist",
"start": "npm run build -s && npm run server -s"
},
3. webpack.config.js
const path = require('path');
var HtmlWebpackPlugin=require("html-webpack-plugin")
module.exports = {
entry: [
'babel-polyfill',
'./src/main.js'
],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
module:{
rules:[
{
test:/\.jsx?$/,
loader:'babel-loader',
exclude: /node_modules/,
options:{
plugins: ['transform-runtime'],
presets: ['es2015']
}
},
{
test: /\.hbs$/,
loader: 'handlebars-loader'
}
]
},
plugins:[
new HtmlWebpackPlugin()
]
};
我们所做的两个更改是将新安装的插件导入文件的顶部,然后在配置对象的末尾添加一个plugins,我们在这个插件中传入新实例。
在这一点上,我们没有将任何选项传递给插件,所以它使用的标准模板不包括很多,但它包含了我们的捆绑脚本。如果您运行npm启动,然后访问浏览器中的URL,您将看到一个空白页面,但是如果您打开开发人员的工具,则应该会将HTML输出到控制台。
我们不应让其只从控制台输出,应该让他出现在HTML中,在src下新建index.html文件
<%= htmlWebpackPlugin.options.title %>
This is my Index.html Template
注意我们通过向ejs文件传递插件中参数
没有指定应该添加脚本的位置。这是因为默认情况下,该插件会将脚本添加到body标签的末尾。
webpack.config.js中增加下面选项
plugins: [
new HtmlwebpackPlugin({
title: 'Intro to webpack',
template: 'src/index.html'
})
]
我在尝试按照上面例子写demo的时候发现并不能将hbs模板插入到html中不知道为啥子.
我知道为啥子了,window.onload!!!
6. 使用懒加载
有一件事我非常喜欢与RequireJS,不能很好地使用Browserify(尽管可能)是懒惰加载模块。一个大规模的JavaScript文件将有助于限制所需的HTTP请求数量,但实际上保证下载的代码不一定在该会话中被访问者使用。
Webpack有一种方法可以将捆绑分割成可以进行延迟加载的大块,甚至不需要任何配置。所有你需要做的是以两种方式之一编写代码,webpack将处理其余的代码。 Webpack为您提供了两种方法:一种基于CommonJS,另一种基于AMD。要使用CommonJS来懒惰加载模块,你可以这样写:
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a");
var b = require("module-b");
// …
});
使用require.ensure,这将确保模块可用(但不执行),并传递一个模块名称数组,然后传回一个回调。要在该回调中实际使用该模块,您需要使用传递给回调的参数来明确地要求它。
AMD版本
require(["module-a", "module-b"], function(a, b) {
// …
});
import { map } from 'lodash';
let numbers = map([1,2,3,4,5,6], n => n*n);
setTimeout( () => {
require(['./numberlist.hbs'], template => {
document.getElementById("app-container").innerHTML = template({numbers});
})
}, 2000);
现在,如果您运行npm start,您将看到生成另一个文件,它应该命名为1.bundle.js。如果您在浏览器中打开页面并打开您的开发工具来观看网络流量,您会看到延迟2秒后,新文件将被加载并执行。这并不是很难实现,可以使用户的体验好多了。
请注意,这些子包或块包含其所有依赖关系,除了每个子包中包含的子包。 (您可以有多个条目,每个条目都延迟加载此块,因此,每个父项都加载不同的依赖项。)
7. Creating A Vendor Chunk
我们来谈一谈可以做的一个优化:wendor。你可以定义要构建的单独的包,该包将存储不太可能更改的“常用”或第三方代码。这允许访问者将库从应用程序代码缓存在一个单独的文件中,以便在更新应用程序时不需要再次下载这些库。
为此,我们将使用一个名为CommonsChunkPlugin的webpack附带的插件。它被包含,我们不需要安装任何东西;我们需要做的是对webpack.config.js进行一些编辑
var HtmlwebpackPlugin = require('html-webpack-plugin');
var UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
entry: {
vendor: ['babel-polyfill', 'lodash'],
main: './src/main.js'
},
output: {
path: './dist',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/,
options: { plugins: ['transform-runtime'], presets: ['es2015'] }
},
{ test: /\.hbs$/, loader: 'handlebars-loader' }
]
},
plugins: [
new HtmlwebpackPlugin({
title: 'Intro to webpack',
template: 'src/index.html'
}),
new UglifyJsPlugin({
beautify: false,
mangle: { screw_ie8 : true },
compress: { screw_ie8: true, warnings: false },
comments: false
}),
new CommonsChunkPlugin({
name: "vendor",
filename: "vendor.bundle.js"
})
]
};
第3行是我们导入插件的地方。然后,在入口部分,我们使用不同的设置(对象字面值)来指定多个入口点。vendor中包括polyfill以及Lodash,我们将主条目文件放入主条目。然后,我们只需要将CommonsChunkPlugin添加到插件部分,将vendor指定为基础的块,并指定vendor将存储在名为vendor.bundle.js的文件中。
通过指定vendor,此插件会将其他条目文件中指定的所有依赖项提取到该供应商块中。如果这里没有指定块名,它将根据条目之间共享的依赖关系创建一个单独的文件。
运行webpack时,现在应该会看到三个JavaScript文件:bundle.js,1.bundle.js和vendor.bundle.js。您可以运行npm开始,并在浏览器中查看结果(如果需要)。看起来webpack甚至会把大部分自己的代码用于处理不同模块的加载到vendor中.
我表示并没有看到1.bundle.js这个文件
结语
当然webpack不仅仅能实现上述功能,他还能轻松实现CSS模块,高速缓存清除散列,图像优化,希望在以后的学习多多运用。( Webpack enables easy CSS modules, cache-busting hashes, image optimization and much much more — so much that even if I wrote a massive book on the subject, I couldn’t show you everything, and by the time I finished writing that book, most (if not all) of it would be outdated! So, give webpack a try today, and let me know if it improves your workflow. God bless and happy coding!)
原文链接