配套可执行代码示例 => GitHub
webpack:前端模块化打包工具
grunt/gulp是项目构建和打包工具,前端自动化任务管理工具。核心是task,我们可以配置一系列的task,并定义task要处理的事务,之后让grunt/gulp来依次执行这些task,并且让整个流程自动化。
grunt/gulp更强调前端流程的自动化,模块化不是它的核心;webpack更强调模块化开发管理,文件压缩合并处理等只是它附带的功能。
应用场景:如果项目的模块依赖非常简单,甚至没有用到模块化的概念,只需要进行简单的合并压缩,就使用grunt/gulp即可;但是如果项目使用了模块化管理,且相互依赖非常强,就可以使用更强大的webpack了。
webpack运行必须依赖node环境,node环境执行很多代码必须包含各种依赖包,这就需要npm(Node Package Manager)工具来管理这些包。
全局安装webpack:
第一步:在src目录下开发源码,可以使用任意模块化开发。具体怎么模块化开发可以看前面这篇笔记:【Vue学习笔记_12】模块化开发
第二步:执行webpack ./src/main.js ./dist/bundle.js
把源码的入口文件main.js及其依赖的文件打包至dist目录下的bundle.js文件中。会自动根据依赖关系从入口文件开始一层层往下面找需要打包的文件,没有被依赖的文件不会被打包
第三步:在html中引入
注:上述的目录及文件名都不是固定的,只是方便示例
用webpack.config.js文件配置打包的入口和出口,这样执行webpack
命令时就不需要带上一大串路径了
//webpack.config.js
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
npm init:初始化项目信息,生成package.json文件,这是npm管理依赖包的文件。
npm install:根据package.json里的依赖,在当前项目中安装一些东西。
终端里直接执行的webpack是全局安装的,而用package.json指定会优先使用局部安装的webpack。一个项目往往依赖特定的webpack版本,全局的webpack版本可能和这个项目的不一致,导出打包出现问题,所以一个项目通常都有自己局部的webpack。
项目中局部安装webpack:npm install webpack@
接着就可以通过node_modules/.bin/webpack
命令启动局部的webpack打包了,但是每次执行都敲这么一大串不太方便。可以在package.json的scripts中定义自己的执行脚本,scripts中的脚本执行时会首先寻找本地的node_modules/.bin/
路径下对应的命令,如果没有找到再去全局的环境变量中寻找。
在下面这个例子中,将webpack命令和build命令对等起来,这样就能运行npm run build
来代替前面的一串命令,构建打包项目了。
//package.json
{
"scripts": {
"build": "webpack"
}
}
loader是webpack中一个非常核心的概念。
之前我们主要用webpack来处理js代码,自动处理js之间的依赖。但是开发中不仅仅有js代码要处理,也需要加载css、图片,也包括将ES6和TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue转成js文件等。webpack本身对这些转化是不支持的,因此需要给webpack扩展对应的loader。
loader一般的使用步骤:
安装:npm install --save-dev css-loader; npm install --save-dev style-loader;
css-loader只负责加载css文件,样式不会生效,style-loader负责将样式添加到DOM中。需要注意的是,使用多个loader时,webpack是从右向左读取的,因此需要把style-loader写在css-loader前面,否则会报错。
接着在webpack.config.js中配置css文件的loader,然后就可以在入口文件中通过require为项目引入css文件了。
//webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
//main.js
require('./css/normal.css')
安装:npm install --save-dev less-loader less
接着在webpack.config.js中配置less文件的loader。less-loader只负责将less文件编译成css文件,因此还需要css-loader、style-loader继续按照加载css文件的步骤处理。然后就可以在入口文件中通过require为项目引入less文件了。
//webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "less-loader"
}]
}
]
}
}
//main.js
require('./css/special.less')
安装:npm install --save-dev url-loader file-loader
在webpack.config.js中配置。当加载的图片小于limit选项值(单位:B)时,url-loader会将图片编译成base64字符串形式;当加载的图片大于limit时,会调用file-loader模块进行处理。
//webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
/*normal.css*/
body {
background: url("../img/wkx.jpg");
}
通过file-loader处理图片会在dist目录下自动生成一个图片文件,以32位hash值命名。这样会导致图片使用的路径不正确,无法显示。因为整个程序是打包在dist目录下的,所以需要配置在url路径前面加上一个dist/
。
//webpack.config.js
module.exports = {
output: {
publicPath: 'dist/'
}
}
在真实开发中,可能对打包的图片名字有一定的要求。比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时为了防止重复再跟上八位hash值,img/name.hash:8.ext
。因此,可以在options中添加选项:
//webpack.config.js
name: 'img/[name].[hash:8].[ext]'
安装:npm install --save-dev babel-loader babel-core babel-preset-es2015
在webpack.config.js中配置。exclude选项用正则表达式指定不需要处理哪些目录下的js文件。
//webpack.config.js
const path = require('path')
module.exports = {
module: {
{
test: /\.js$/,
//不需要转化node_modules、bower_components目录下的js文件
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
}
安装:npm install vue --save
(因为后续在实际项目中也会使用 vue,所以不再是开发时依赖)
在入口文件中导入:
//main.js
import Vue from 'vue'
但是打包运行报错,因为webpack默认使用runtime-only版本的vue,代码中不可以有任何template。因此需要在webpack.config.js中为vue配置alias别名,指定使用runtime-compiler版本的vue,这样就可以编译template了。
//webpack.config.js
module.exports = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
}
SPA(single page web application):单页面富应用,项目只有一个html文件,通过前端路由动态更新页面。
创建Vue时,如果同时有el和template,页面显示时template里的内容会替换掉el挂载的元素,这样的好处是不需要频繁修改html代码。
<div id="app">
div>
//main.js
new Vue({
el: '#app',
template: `
{{msg}}
`
})
webpack集成vue的最终方案:.vue文件
安装:npm install vue-loader vue-template-compiler --save-dev
//webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: [ 'vue-loader' ]
},
]
},
resolve: {
//配置可以在引入哪些类型的文件时省略扩展名
extensions: ['.js','.css','.vue']
}
}
//main.js
import Vue from 'vue'
import App from './vue/App'
new Vue({
el: '#app',
template: ' ',
components: {
App
}
})
<template>
<div>
<h2 class="title">{{msg}}h2>
<button @click="btnClick">按钮button>
div>
template>
<script>
//这里可以import其他vue组件
export default {
name: "App",
data() {
return {
msg: 'Hello'
}
},
methods: {
btnClick() {
console.log('ok');
}
}
}
script>
<style scoped>
.title {
color: yellow;
}
style>
plugin插件:对webpack现有功能的各种扩展
为打包的文件添加版权声明(开源许可证/开源协议);是webpack的自带插件。
//webpack.config.js
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.BannerPlugin('最终版权归 BugFrog1024 所有')
]
}
当前index.html文件是放在项目的根目录下的,真实发布项目时,发布的是dist目录中的内容,所以我们需要将index.html打包到dist目录中,这个时候就可以使用HtmlWebpackPlugin插件了。它可以由指定的模板自动生成一个index.html文件,将打包的js文件自动通过script标签插入到body中。
不是webpack的自带插件,需要安装:npm install html-webpack-plugin --save-dev
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// output: {
// publicPath: 'dist/'
// },
plugins: [
new HtmlWebpackPlugin({
//指定根据哪个模板来生成dist目录下的index.html
template: 'index.html'
})
]
}
另外,需要删除之前在output中添加publicPath属性,否则自动插入的script标签中的src可能会有问题,因为把index.html打包到dist目录下之后,就不再需要在url前面加上dist/
路径了。
对打包的js文件进行压缩:删除空格,替换变量名,删除注释(也包含了添加的版权声明)
安装:npm install uglifyjs-webpack-plugin --save-dev
//webpack.config.js
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new UglifyjsWebpackPlugin()
]
}
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现让浏览器自动刷新显示我们修改后的结果。
安装:npm install webpack-dev-server --save-dev
//webpack.config.js
module.exports = {
derServer: {
//为哪个目录提供本地服务,默认是根目录
contentBase: './dist',
//是否实时刷新页面
inline: true
//服务端口号,默认8080
port: 8080
}
}
//package.json
{
"scripts": {
"dev": "webpack-dev-server --open"
}
}
在package.json中配置scripts脚本后,就可以通过npm run dev命令启动本地服务器了。在本地服务器上测试好代码之后,再用npm run build命令对项目最终打包。
需求:有的配置是开发时需要的,比如webpack-dev-server;有的配置是发布时需要的,比如uglifyjs-webpack-plugin。因此需要分离,开发时依赖一个配置文件,发布时依赖另一个。
webpack-merge:用于合并webpack配置。
安装:npm install webpack-merge --save-dev
在项目下创建一个build目录,专门放配置文件:
//base.config.js
module.exports = {
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.js',
}
}
//dev.config.js和prod.config.js
const WebpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = WebpackMerge(baseConfig, {
...
})
在package.json中配置scripts脚本:
//package.json
{
"scripts": {
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
}
}