文章同步发表在:博客地址javascript
关于为何要使用webpack,我比较认同的一种说法是:css
webpack能够很好地管理你开发中遇到的各类HTML、JS、CSS以及各类图片资源文件,同时对应不一样的资源,webpack还提供了对应的Loaders将其进行转化为适用于浏览器使用的格式html
后会无期里面,阿吕说过这么一句话:"你连世界都没有观过,哪里来的世界观",在咱们实际的项目开发中,好比你用的是vue,那么vue已经有很好的脚手架工具(vue-cli)供你使用了,或者有的开发团队,会有技术Leader专门预先作好相关的模板,方便后来新加入的成员尽快上手项目,可是随着你开发的项目越多,你可能会越不注意到那些最基本的东西,好比说这个模板究竟是如何搭建的,或者说让你本身来搭建一个模板给其余人用,是否也能作到如此的简单易上手,我想这是每个想要在前端路上进阶的人须要考虑的问题。前端
最好的方式就是本身试着去搭一个最简单的模板,从0到1的过程是最痛苦的,可是1到2或者说2到3都是水到渠成的事,下面就看看怎么开始从0搭建一个基于webpack的vue的开发环境vue
注:使用的webpack版本为^3.10.0 首先经过webstorm或者你手上的其余IDE建立一个空的项目,我这里暂且叫proWebpack,建立好后是这个样子的:java
由于咱们经过npm来管理包,而后每一个项目的根目录下都会有一个package.json
文件来管理项目的配置信息,包括名称、版本、许可证等元数据以及记录所需的各类模块,包括 执行依赖,和开发依赖,咱们能够经过在命令控制台用npm init
命令建立一个package.json
文件,可是这样很麻烦,由于这样npm
会经过命令行问答的方式来初始化并建立package.json
文件,为了方便起见咱们使用npm init -y
,这样npm
就会使用一些默认值进行初始化:node
ok如今咱们获得一个package.json文件,包含了一些简单的信息好比name、version等字段,接下来,由于咱们是想搭建一个用于vue开发环境的webpack目录结构,到目前为止还没看到vue的影子,依然是在命令行使用npm install vue --save
,这里注意package.json
中的依赖包有dependencies
和devDependencies
两种,这两种的区别:dependencies
表示咱们要在生产环境下使用该依赖,devDependencies
则表示咱们仅在开发环境使用该依赖,咱们install时候使用--save会把包安装在dependencies 下面,因此执行完上面的命令你能够看到这样的目录结构:webpack
{
"name": "proWebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^2.5.16"
}
}
复制代码
而后在devDependencies
下面安装webpack,再次提醒,因为webpack4变化较大,这里使用^3.10.0版本的webpack: npm install -D webpack@3.10.0
,此时目录结构以下:git
{
"name": "proWebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^2.5.16"
},
"devDependencies": {
"webpack": "^3.10.0"
}
}
复制代码
接下来建立入口文件,在根目录下建立一个index.html做为启动页面,一个webpack.config.js做为webpack配置文件(实际项目中这里会有webpack配置文件,分别用于开发环境和生产环境,这里简便起见就用一个配置文件),新建一个src目录,在该目录下新建一个index.js做为打包入口文件:github
proWebpack
├─ index.html 启动页面
├─ package-lock.json
├─ package.json 包管理
├─ src
│ └─ index.js 入口文件
└─ webpack.config.js webpack配置文件
复制代码
为了更接近vue-cli建立出来的模板,咱们还须要建立一个Vue实例提供给入口文件的el挂载,这个Vue文件很简单长这样:
<template>
<div>proWebpack</div>
</template>
复制代码
而后写好入口文件:
import Vue from 'vue'
import App from '../App.vue'
new Vue({
el: '#app',
render: h => h(App)
})
复制代码
同时别忘记在Index.html里面要提供一个供vue实例挂载的HTMLElement实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>
复制代码
这样准备工做作得差很少了,下面作最核心的部分
webpack有这么几个核心的概念:
下面开始配置,由于全部输出文件的目标路径必须是绝对路径,因此这里要用到使Node.js 的 path 模块
// 引入webpack和path模块
const path = require('path')
const webpck = require('webpack')
复制代码
简单配置文件的entry和output
const path = require('path')
const webpck = require('webpack')
const productionPath = require('./package.json').name
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.join(__dirname, 'dist', productionPath),
filename: 'bundle.js'
}
}
复制代码
这里代码应该不难理解,就是从index.js
这个入口进去,把直接或间接依赖项打包输出dist文件夹目录下, 怎么运行呢,咱们须要在package.json
的scripts对象里面添加咱们本身的build命令:
// 这里用最简单的命令
"build": "webpack -w",
复制代码
由于咱们的webpack配置文件就叫webpack.config.js,默认状况下,webpack在执行的时候会搜索当前目录webpack.config.js 文件执行,实际开发中咱们会使用 --config 选项来指定配置文件来对开发环境和生产环境的配置作出区分
直接在控制台npm run build
,果不其然报错了:
ERROR in ./App.vue
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| <template> | <div>proWebpack</div> | </template>
@ ./src/index.js 2:0-28
复制代码
很明显,webpack告诉咱们他无法识别vue文件,须要咱们提供一个相关的loader来处理成webpack能够识别的文件,这里先npm install vue-loader -D
,而后回到webpack.config.json
配置loader:
const path = require('path')
const webpck = require('webpack')
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
// 模块配置
rules: [
//模块规则(配置 loader、解析器等选项)
{
// 这里是匹配条件,每一个选项都接收一个正则表达式或字符串
// test是必须匹配选项
// exclude 是必不匹配选项(优先于 test 和 include)
// 对选中后的文件经过 use 配置项来应用 Loader,能够只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还能够分别给 Loader 传入参数。
test: /\.vue$/,
exclude: /node_modules/,
use: {loader: "vue-loader"}
}
]
}
}
复制代码
配置好以后再run一遍,咦,又报错,咱们看下主要的报错信息:
ERROR in ./App.vue
Module build failed: Error: Cannot find module 'vue-template-compiler'
at Function.Module._resolveFilename (module.js:527:15)
at Function.Module._load (module.js:476:23)
复制代码
cannot fount那就是须要咱们去install呗,可是为何要下这个模块,我找到一个比较靠谱的说法:
其中 vue-template-compiler 是 vue-loader 的 peerDependencies,npm3 不会自动安装 peerDependencies,然而 vue-template-compiler 又是必备的,那为何做者不将其放到 dependencies 中呢?有人在 github 上提过这个问题,我大体翻译一下做者的回答(仅供参考):这样作的缘由是由于没有可靠的方式来固定嵌套依赖的关系,怎么理解这句话?首先 vue-template-compiler 和 vue 的版本号是一致的(目前是同步更新),将 vue-template-compiler 指定为 vue-loader 的 dependencies 并不能保证 vue-template-compiler 和 vue 的版本号是相同的,让用户本身指定版本才能保证这一点。查看做者的回答(英文) 。若是二者版本不一致,运行时就不会错误提示。
那只须要咱们npm i vue-template-compiler -D
一下,而后在跑一遍build脚本,不出意外你应该能在项目根目录看到一个dist文件夹,把这个文件夹里面的js文件引入启动页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
<script src="/dist/bundle.js"></script>
</body>
</html>
复制代码
接下来咱们要访问这个页面,这里用到webpack提供的devsever来调试咱们的页面, DevServer 会启动一个 HTTP 服务器用于服务网页请求,同时会帮助启动 Webpack ,并接收 Webpack 发出的文件更变信号,经过 WebSocket 协议自动刷新网页作到实时预览。这就是为何你用vue-cli搭建出来的脚手架能够作到页面实时渲染,配置这个也很简单,在webpack.config.js
里面添加以下配置:
devServer: {
port: 3000, // 端口号
}
复制代码
其实webpack-dev-server能够配置不少参数,这里不过多展开,接下来须要修改下咱们的build脚本:
"build": "webpack-dev-server"
复制代码
别忘了npm install
一下webpack-dev-server
,最后执行npm run build看到控制台报出成功信息并告诉你你的项目正运行在localhost:3000:
$ npm run build
> proWebpack@1.0.0 build D:\proWebpack
> webpack-dev-server --open
Project is running at http://localhost:3000/
webpack output is served from /
复制代码
访问localhost:3000看到以下显示的话,恭喜你,webpack配置如何从0到1你应该已经清楚了:
咱们知道了怎么初步配置webpack还远远不够,实际开发中咱们会遇到更多样的状况,好比说 当咱们只有一个输出文件的时候咱们能够在output写死,就像咱们上面写的:
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
}
复制代码
可是当咱们有多个文件输出的时候,一个个去写是一个费力不讨好的作法,这个时候就要用到webpack内置的变量了,咱们能够这么写:
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[hash].js'
}
复制代码
这样会赋予每个bundle惟一的名称,而且在每次构建过程当中,生成惟一的 hash ,这时候稍微改动一下脚本文件:
"scripts": {
"start": "webpack-dev-server",
"build": "webpack -w",
"test": "echo \"Error: no test specified\" && exit 1"
},
复制代码
以前写一块儿是为了方便理解, 如今我把跑本地服务器和打包的脚本拆开写,更语义化,通常正常开发也是这么作的,先跑一下build,你会发现此时dist文件夹下面生成了新的打包文件index.8ad46f4fb5c8385db614.js
,而后把这个文件一样引入index.html
,跑start脚本发现结果也是照常输出,可是又有了新的问题,若是咱们每次build以后都要手动引入带着一长串hash的js文件也是很蠢的事情,因此这里咱们能够用上一个plugin叫作html-webpack-plugin
,这个插件主要有两个做用: 一、在每次编译完成以后动态为html文件引入外部资源(script、link),对于我me你这种带hash的文件名来讲无疑是极为方便的 二、能够制定一个模板的html文件,html-webpack-plugin能够根据这个模板来生成html入口文件
下面看下如何使用:
// 引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
//省略若干代码
plugins: [
new HtmlWebpackPlugin({
filename: './index.html', // 生成的入口文件的名字,默认就是index.html
template: './index.tpl.html',// 有时候,插件自动生成的html文件,并非咱们须要结构,咱们须要给它指定一个模板,让插件根据咱们给的模板生成html
inject: 'body',// 有四个选项值 true, body, head, false----true:默认值,script标签位于html文件的 body 底部 body:同true head:插入的js文件位于head标签内 false:不插入生成的js文件,只生成一个纯html
})
],
复制代码
用到的index.tpl.html
只是和以前的index.html
做了一点小小的区分,让人知道这是模板生成的:
// index.tpl.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是模板生成的</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>
复制代码
方便起见咱们在package.json
在添加一个clean的脚本,每次build以前先删除以前生成的dist文件:
"scripts": {
"start": "webpack-dev-server",
"build": "npm run clean && webpack -w",
"clean": "rimraf dist", // 添加clean脚本
"test": "echo \"Error: no test specified\" && exit 1"
}
复制代码
而后run build一下:
其实这里还有一点没有提到的就是css的处理,除了css-loader咱们日常开发还会用到一些css预处理器如scss,还有postcss等样式处理工具,这里的处理其实也不难,感兴趣的朋友能够本身尝试一下,这里因为篇幅缘由(我想偷懒)就不过多展开了。