扯淡的话
扯淡1
我同事问我为什么要用webpack,我自己也在思考为什么。上网也看了很多前辈的见解,也自己思考和比较后。总结下来就和我们用智能手机一样,习惯后,再去拿个普通手机怎么都是不方便的。这一个工具结合其loaders和plugins再配合上其他工具,就像是一个超级强大的智能手机。如果你还在用着老式的手机,可能这强大到你无从下手的东西让你开始有些抗拒,不过一旦你开始用它,你将会爱不释手。扯淡2
我一直是把webpack当作一个工具,再加上vue-cli的webpack配置很完善,所以最开始我是拒绝好好学习一下的。不过看着vue-cli中把webpack配置的那么井井有条,又看了看自己的需要不断补全的webpack配置文件。我又忍不住摩拳擦掌想去尝试一下。
开始的话
为了方便自己知识的梳理和更加深入的理解一个有模有样的webpack配置的构成。我决定从0开始。所以有可能这篇文章还是在编写中,但会一直更新到我认为这份webpack配置已经能够面向生产使用了。
还有就是这份webpack配置是围绕vue项目来展开。而且,本着学新不学旧的原则,我使用的是webpack4版本。我总结的东西可能只是方便我自己理解的,当然,我说到的所有配置在webpack文档中,你基本都可以找到。
从0开始--基础部分
第一个需求
我期望可以在我的html中仅仅引入一个js文件而不是像很多比较老的项目一样引入长长的一排js
先针对这个问题。简单的来实现一个由webpack构建的小应用。
在此之前,我假设你已经知道了一些webpack的知识。首先要进行项目依赖安装。webpack4还需要安装webpack-cli。
分析项目
这个小型的项目对于webpack来说,只需要一个输入和输出就好了。
项目结构:
- dist为打包好后的bundle.js存放目录(bundle也就是捆绑~的意思,就是把所有的模块都捆绑到这一个模块中)
- src 存放源码的目录 index.js 为入口文件 依赖于document.js(超级简化版jq) 和 alert.js(超级简化版jq插件)。这是不是很符合以前使用jq开发的模式,现在我们利用webpack让我们只需要关注index.js即可
- 其他文件。。。
webpack配置代码,只有输入输出:
const path = require('path') // 使用path是为了
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'), //
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
运行 webpack --config webpack.conf.js
在dist文件中得到bundle.js
并用index.html
引用它即可
为了方便,配置build命令: webpack--mode production --config webpack.conf.js
关于mode,后面会说到
第二个需求
我看着打包后的bundle.js
的代码
!function(e) {
// 此处省略n行webpack构建的模块化代码
}([function (e, t, n) {
"use strict";
n.r(t);
// 简化版的jq
class r {
constructor() {}
static getEle(e) {
return document.querySelector(`${e}`)
}
static getEles(e) {
return document.querySelectorAll(`${e}`)
}
}
// index代码
r.getEle("#btn").addEventListener("click", e => {
// 简化版jq插件代码
new class {
constructor(e) {
const t = document.createElement("div");
t.innerHTML = `\n \n \n ${e}\n \n \n `, r.getEle("body").appendChild(t)
}
}("哈哈哈")
})
}])
我感到一丝的不安,这些花里胡哨的ES6代码再chrome上固然是没问题的,但我还是希望其可以转化为ES5代码,这样更为通用一些,同时,一些H5的新API(例如fetch)可以注入polyfill解决兼容性问题。这是第二个需求。
现在,只是依赖webpack的输入输出的基本能力已经解决不了这个问题了。需要引入webpack最强大的一个工具: loader。
我理解的webpack中的loader像一条流水线,不同的产品(代码)会交给不同的流水线(loader)处理。这里我们使用Babel和babel-loader来解决这一问题。
babel小插曲1
简介: babel
是一个JavaScript编译器,可将ES6代码转换为ES5代码。
简单使用: 通过配置.babelrc
配置文件(JSON格式)。 关于.babelrc
配置详解会在后面补充,这里暂时不需要掌握这么多
安装babel依赖:这里需要安装三个babel的依赖:
npm i --save-dev babel-core babel-loader babel-preset-env // babel-preset-env可以根据支持的环境来自动决定适合的Babel插件
// 最简单的.babelrc配置:
{
"presets": [ // presets作为一个数组,用来告诉babel需要对哪些语音新特性提供支持
"env"
]
}
修改webpack配置文件
const path = require('path')
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/, // 提供正则来匹配.js结尾的文件
use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
}
]
}
}
ok,这样在通过babel处理后的ES6代码就转换为了ES5的通用代码(转换后的代码不做展示了)
第三个需求
我想我要开始使用框架了,人家都用vue,我也要尝试下。
一开始也像前面那样引入js文件一样引入vue
import {alert} from './libs/alert.js'
import Vue from 'vue/dist/vue.js' // 关于这里有个问题,大家可以将/dist/vue.js去掉看一下会有什么问题
const vm = new Vue({
el: '#app',
data: {
},
methods: {
showModel(str) {
new alert(`你好${str}`)
}
}
})
但是这样终究没办法让开发者更好地利用vue组件化的威力。何不像vue-cli的项目那样,使用.vue
文件来开发项目呢?
vue-loader
上面介绍过一个处理js文件的loader,那么对于.vue
文件,官方也有一个对应的vue-loader
来处理它
npm i vue-loader css-loader vue-template-compiler --save-dev // 由于vue-loader依赖于css-loader vue-template-compiler 用于将vue-loader提取的html模板编译为对应的JavaScript代码
现在自己定义一个通过.vue
构成的项目
/*
index.js 代码
*/
import Vue from 'vue/dist/vue.esm'
import App from './App.vue'
const vm = new Vue({
el: '#app',
components: { App },
template: ' '
})
/*
webpack 配置
*/
const path = require('path')
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/, // 提供正则来匹配.js结尾的文件
// use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
loader: 'babel-loader'
},
{
test: /\.vue$/, // 匹配.vue结尾的文件
loader: 'vue-loader'
}
]
}
}
踩坑1
这时候,出问题了。
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
在webpack4中编译打包vue文件的时候需要引入VueLoaderPlugin
而这里,也要引入webpack的第二个利器,plugins。我理解的plugins就像是工厂中的一些处理工具,是辅助流水线(loader)来更好的处理代码的存在。
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/, // 提供正则来匹配.js结尾的文件
// use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
loader: 'babel-loader'
},
{
test: /\.vue$/, // 匹配.vue结尾的文件
loader: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
}
这时,运行这个webpack配置文件又出错了:
对于.vue
文件中的css代码,需要css-loader来处理。在上面代码中补上这样一段代码:
{
test: /\.css$/,
loader: 'css-loader'
}
打包后~~ 为什么样式没有生效啊。这里需要再加上一个style-loader
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}
一般来说需要引入css-loader和style-loader,其中css-loader用于解析,而style-loader则将解析后的样式嵌入js代码。
到这,很简单的一个vue组件已经可以展示出来了。
第四个需求
用上vue后,我迫不及待的写了几行代码,却发现每次要验证代码的效果都需要执行一次webpack的配置文件。这让我很苦恼。我希望我每次改完我的代码都可以在页面中直接体现出来。
webpack的开发者服务器亮相了:npm i webpack-dev-server --save-dev
devServer文档说明
在写配置之前,使用devServer的一些基本东西我想了解到:
- 1、如何启动开发服务器
- 2、如何配置开发服务器的例如host和port等配置项
- 3、我们启动的服务器是以哪个html文件为模板显示在浏览器中的。如何配置这个模板
使用开发服务器来启动项目,那自然命令和之前的不同,那配置一个dev命令来表示以devServer的模式启动项目: webpack-dev-server --mode development --config webpack.conf.js
。
现在可以启动服务器后,可以看到通过浏览器自动打开了一个页面:
webpack提供一个devServer属性,可以在其中方便的配置devServer的各种属性,像页面中体现的localhost(host属性)和8080(port)都是可以自定义来配置的,除此之外,也可在前端使用devServer来进行代理配置等等,属性很多,功能很强大,请参考devServer文档说明,这里不一一赘述。
最后一个疑惑?如何去配置模板页面?devServer的historyApiFallback
属性可配置在URL命中不同规则时返回不同的页面,当然也可以用最简单的historyApiFallback: true
来规定无论什么URL,都返回index.html
。我把之前的index.html清理一下作为devServer的模板页面:
Document
除了创建模板页面外,还需要html-webpack-plugin
插件。因为我们的模板页面是没有引入我们编译后的文件,所以需要这个插件来根据模板html创建一个引入了编译后的js,css文件的html文件并将其展示到浏览器中。这个插件当然不仅仅是这一点作用,这里我们只需要明白它的这一用处即可。
修改webpack配置文件:
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/, // 提供正则来匹配.js结尾的文件
// use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
loader: 'babel-loader',
exclude: '/node_modules/'
},
{
test: /\.vue$/, // 匹配.vue结尾的文件
loader: 'vue-loader',
exclude: '/node_modules/'
},
{
test: /\.css$/,
loader: 'style-loader!css-loader',
exclude: '/node_modules/'
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: 'index.html' // 告诉HtmlWebpackPlugin插件使用哪个模板
})
],
devServer: {
historyApiFallback: true,
}
}
执行npm run dev
:
修改一下代码:
这将会大大提升开发效率
基础部分完成
到这里,基础部分的内容就完成了。现在的这个webpack配置是非常粗糙但是能完成基础的需求了。接下来我会结合Vue完成进阶部分的内容。
第二篇地址
最后github地址,求star