一、webpack Loader的工作原理
实现一个markdown的文件加载器。
Loader作为webpack的核心机制,内部的工作原理也非常简单,我们通过开发一个自己的loader,来深入了解loader的工作原理。
需求:实现一个markdown文件的加载器,这个加载器可以在代码当中直接导入markdown文件。markdown文件一般是被转换为html过后再去呈现到页面上的,所以我们导入的markdown文件得到的结果就是转换过后的html字符串。
在项目的根目录下新建一个markdown-loader.js文件,每一个webpack的loader都需要去导出一个函数,这个函数是loader的对所加载到的资源的一个处理过程,它的输入就是加载到的资源文件的内容,输出就是加工过后的一个结果。
module.exports = source => {
console.log(source)
return 'hello'
}
module: {
rules: [
{
test: /.md$/,
use: './markdown-loaderr' //use属性不仅可以写已经在webpack中的loader,还可以写目录下的
}
]
}
webpack的加载资源的过程类似于一个工作管道,可以在这个过程当中一次去使用多个loader,但是最终这个管道工作过后的结果必须是一段JavaScript代码
第1种办法:
module.exports = source => {
console.log(source)
return 'console.log("hello")'
}
打开bundle.js,webpack打包把我们刚刚loader加载过后的结果直接拼接到我们这个模块当中了,也就解释了loader的管道最后必须要返回JavaScript代码的原因,如果随便去返回一个内容的话,那放到这里语法就有可能不通过。
回到markdown-loader.js的当中,先去安装一个markdown解析的模块叫做marked,安装命令为:yarn add marked —dev
marked方法返回值是一段html字符串,接下来把这段html变成一段JavaScript代码,
通过JSON.stringify先将这个字符串转换为一个标准的JavaScript格式字符串,那此时内部的引号以及换行符都会被转义过来,然后我们再参与拼接,那这样的话就不会有问题了
再次运行打包,看一下打包的结果,那此时我们所看到的结果就是我们所需要的了,当然了,除了module.exports这种方式外,webpack的还允许我们在返回的代码当中直接去使用ESModule的方式去导出.
const marked = require('marked')
module.exports = source => {
const html = marked(source)
console.log(html)
// 两种导出方式:
// return `module.exports=${JSON.stringify(html)}`
return `export default ${JSON.stringify(html)}`
}
第2种方法:
那就是在我们markdown-loader的当中去返回一个html的字符串,然后我们交给下一个loader处理这个htm的字符串
安装一个用于去处理html加载的loader,叫做html-loader
const marked = require('marked')
module.exports = source => {
const html = marked(source)
console.log(html)
return html
}
module: {
rules: [
{
test: /.md$/,
use: ['html-loader', './markdown-loader.js']
}
]
}
loader,它实际上是一种管道的概念,我们可以将我们此次的这个loader的结果交给下一个loader去处理,然后我们通过多个loader去完成一个功能,如之前所使用的css-loader和style-loader之间的一个配合,包括我们后面还会使用到的,像sass或者less这种loader他们也需要去配合我们刚才所说道的这两种loader,这就是我们loader的工作管道这样一个特性。
二、Webpack插件机制
插件机制的是webpack
一个核心特性,目的是为了增强webpack
自动化方面的能力。
Loader专注实现资源模块的加载,从而去实现整体项目的打包。
Plugin解决除了资源加载以外的其他的一些自动化工作:
1、clean-webpack-plugin
自动在打包之前去清除dist目录
安装:yarn add clean-webpack-plugin --dev
webpack.config.js
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin()
]
2、html-webpack-plugin
自动生成使用bundel.js的HTML
安装:
yarn add html-webpack-plugin --dev
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
自动生成
HTML
文件到dist
目录中,根目录下的index.html则不再需要了- HTML中自动注入了
bundle.js
的引用到HTML
文件中 增加配置参数生成HTML文件:
new HtmlWebpackPlugin({ title: 'Webpack Plugin Sample', meta: { viewport: 'width=device-width' } })
通过模板文件生成HTML文件, webpack.config.js中指定HtmlWebpackPlugin的template参数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack</title> </head> <body> <div class="container"> <h1><%= htmlWebpackPlugin.options.title %></h1> </div> </body> </html>
plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Webpack Plugin Sample', meta: { viewport: 'width=device-width' }, template: './src/index.html' }) ]
生成多个HTML页面
plugins: [ new CleanWebpackPlugin(), // 用于生成index.html new HtmlWebpackPlugin({ title: 'Webpack Plugin Sample', meta: { viewport: 'width=device-width' }, template: './src/index.html' }), // 用于生成about.html new HtmlWebpackPlugin({ filename: 'about.html' }) ]
3、copy-webpack-plugin
拷贝那些不需要参与打包的资源文件到输出目录,压缩我们打包结果输出的代码
安装:yarn add copy-webpack-plugin --dev
因为这些资源文件一般都比较大,所以插件只会在上线前打包使用,开发过程中经常会打包编译,执行这个插件太消耗资源了 ```javascript const CopyWebpackPlugin = require(‘copy-webpack-plugin’)
new CopyWebpackPlugin( { patterns: [‘public’] } //[‘public’] )
<a name="ZL6wX"></a>
# 三、webpack开发插件
相比于Loader,Plugin拥有更宽的能力范围,Plugin通过钩子机制实现。<br />webpack在工作时有很多环节,便于插件的扩展,几乎在每一个环境都安排了钩子<br />Webpack要求插件必须是一个函数或者是一个包含apply方法的对象。通过在生命周期的钩子中挂载函数实现扩展。
```javascript
class MyPlugin {
apply (compiler) {
console.log('MyPlugin 启动')
compiler.hooks.emit.tap('MyPlugin', compilation => {//emit钩子,在输出文件之前
// compilation 可以理解为此次打包的上下文
for (const name in compilation.assets) {
// console.log(name) // 文件名
// console.log(compilation.assets[name].source())
if(name.endsWith('.js')) {
const contents = compilation.assets[name].source()
const withoutComments = contents.replace(/\/\*\*+\//g, '')
compilation.assets[name] = {
source: () => withoutComments,
size: () => withoutComments.length
}
}
}
})
}
}
plugins: [
new MyPlugin()
]
四、webpack dev server
自动进行编译:执行yarn webpack —watch会监视文件的变化自动进行打包
自动刷新浏览器:webpack-dev-server,安装:yarn add webpack-dev-server —dev
"scripts":{
"dev":"webpack server"
}
-----------
"dev": "webpack server --open --host 127.0.0.1 --port 9999"
//--host 配置ip --port配置端口
Dev Server默认只会serve打包输出文件,只要是webpack打包输出的文件都会被访问到,其他静态资源也需要被server
devServer: {
contentBase: './public',//contentBase额外为开发服务器指定查找资源目录
open:true,
},
target: 'web' //5以上版本要指定是在web环境下,不然不会自动刷新
五、proxy代理
webpack-dev-server支持配置代理
devServer: {
contentBase: './public',
proxy: {
'/api': {// 以/api开头的地址都会被代理到接口当中
// http://localhost:8080/api/users -> https://api.github.com/api/users
target: 'https://api.github.com',
// http://localhost:8080/api/users -> https://api.github.com/users
pathRewrite: {
'^/api': ''
},
// 不能使用localhost:8080作为请求GitHub的主机名
changeOrigin: true, // 以实际代理的主机名去请求
}
}
}