webpack系列2:loader

Webpack中必须掌握的配置

loader主要用于把模块原内容按照需求转换成新内容,可以加载非 JS 模块!
通过使用不同的Loader,Webpack可以把不同的文件都转成JS文件,比如CSS、ES6/7、JSX等。

我们来看看这些我们必须掌握的loader!

1.loader的编写

1.1 loader的使用

  • test:匹配处理文件的扩展名的正则表达式
  • use:loader名称,就是你要使用模块的名称
  • include/exclude:手动指定必须处理的文件夹或屏蔽不需要处理的文件夹
  • options:为loaders提供额外的设置选项

默认loader的顺序是从下到上、从右向左执行,当然执行顺序也可以手动定义的,接下来我们依次介绍常见的loader,来感受loader的魅力!

我们基于这个基础配置来继续编写:

const path = require("path"); const dev = require("./webpack.dev"); const prod = require("./webpack.prod"); const merge = require("webpack-merge"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const base = { entry:'./src/index.js', output: { filename: "[name].js", path: path.resolve(__dirname, "../dist") }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: path.resolve(__dirname, "../public/template.html"), hash: true, minify: { removeAttributeQuotes: true } }), new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: [path.resolve('xxxx/*'),'**/*'], }), ] }; module.exports = env => { if (env.development) { return merge(base, dev); } else { return merge(base, prod); } };

2.处理CSS文件

2.1 解析css样式

我们在js文件中引入css样式!

import './index.css';

再次执行打包时,会提示css无法解析

ERROR in ./src/index.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

安装loader

npm install style-loader css-loader --save-dev
module: {
  rules: [
    {
       test: /\.css$/, use: ["style-loader", "css-loader"] } ] }

2.2 抽离样式文件

默认只在打包时进行样式抽离

module.exports = env => { let isDev = env.development; const base = {/*source...*/} if (isDev) { return merge(base, dev); } else { return merge(base, prod); } };

安装抽离插件

npm install mini-css-extract-plugin --save-dev

配置抽离插件

{
    test: /\.css$/, use: [ !isDev && MiniCssExtractPlugin.loader, isDev && 'style-loader', "css-loader" ].filter(Boolean) } !isDev && new MiniCssExtractPlugin({ filename: "css/[name].css" })

最终文件配置贴一下:

const path = require("path"); const dev = require("./webpack.dev"); const prod = require("./webpack.prod"); const merge = require("webpack-merge"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = env => { let isDev = env.development; const base = { entry: "./src/index.js", output: { filename: "[name].js", path: path.resolve(__dirname, "../dist") }, module: { rules: [ { test: /\.css$/, use: [ !isDev && MiniCssExtractPlugin.loader, isDev && 'style-loader', "css-loader" ].filter(Boolean) } ] }, plugins:[ !isDev && new MiniCssExtractPlugin({ filename: "css/[name].css" }), new HtmlWebpackPlugin({ filename: "index.html", template: path.resolve(__dirname, "../public/template.html"), hash: true, minify: { removeAttributeQuotes: true } }), ].filter(Boolean) }; if (isDev) { return merge(base, dev); } else { return merge(base, prod); } };

2.3 css预处理器

不同的css预处理器要安装不同的loader来进行解析

  • sass: sass-loader node-sass
  • less: less-loader less
  • stylus: stylus-loader stylus

使用sass

{
    test:/\.scss$/, use:[ !isDev && MiniCssExtractPlugin.loader, isDev && 'style-loader', "css-loader", "sass-loader" ].filter(Boolean) }

在css文件中可能会使用@import语法引用css文件,被引用的css文件中可能还会导入scss

{
    test: /\.css$/, use: [ !isDev && MiniCssExtractPlugin.loader, isDev && 'style-loader', { loader:"css-loader", options:{ importLoaders:1 // 引入的文件需要调用sass-loader来处理 } }, "sass-loader" ].filter(Boolean) },

2.4 处理样式前缀

使用postcss-loader增加样式前缀

npm install postcss-loader autoprefixer

在处理css前先增加前缀

 {
    test: /\.css$/, use: [ !isDev && MiniCssExtractPlugin.loader, isDev && 'style-loader', { loader:"postcss-loader", options:{ plugins:[require('autoprefixer')] } }, "postcss-loader", "sass-loader" ].filter(Boolean) },

或者也可以创建postcss的配置文件postcss.config.js

module.exports = {
    plugins:[ require('autoprefixer') ] }

可以配置浏览器的兼容性范围 .browserslistrc

cover 99.5%

2.5 css压缩

在生产环境下我们需要压缩css文件,配置minimizer选项,安装压缩插件

npm i optimize-css-assets-webpack-plugin terser-webpack-plugin --save-dev

webpack.prod.js文件中配置压缩

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const TerserJSPlugin = require('terser-webpack-plugin'); optimization:{ minimizer:[new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})] }

2.6 文件指纹

  • Hash整个项目的hash值
  • chunkhash 根据入口产生hash值
  • contentHash 根据每个文件的内容产生的hash值

我们可以合理的使用hash戳,进行文件的缓存

!isDev && new MiniCssExtractPlugin({ filename: "css/[name].[contentHash].css" })

3.处理文件类型

3.1 处理引用的图片

import logo from './webpack.png';
let img = document.createElement('img'); img.src = logo; document.body.appendChild(img);

使用file-loader,会将图片进行打包,并将打包后的路径返回

{
    test:/\.jpe?g|png|gif/, use:{ loader:'file-loader', options:{ name:`img/[name].[ext]` } } }

3.2 处理icon

二进制文件也是使用file-loader来打包

{
    test:/woff|ttf|eot|svg|otf/, use:{ loader:'file-loader' } }

3.3 转化成base64

使用url-loader将满足条件的图片转化成base64,不满足条件的url-loader会自动调用file-loader来进行处理

{
    test:/\.jpe?g|png|gif/, use:{ loader:'url-loader', options:{ limit:100*1024, name:`img/[name].[ext]` } } }

4.处理JS模块

4.1 将es6代码编译成es5代码

代码的转化工作要交给babel来处理

npm install @babel/core @babel/preset-env babel-loader --save-dev

@babel/core是babel中的核心模块,@babel/preset-env 的作用是es6转化es5插件的插件集合,babel-loaderwebpackloader的桥梁。

const sum = (a, b) => { return a + b; };

增加babel的配置文件 .babelrc

{
    "presets": [
       ["@babel/preset-env"] ] }

配置loader

module: {
	rules: [{ test: /\.js$/, use: "babel-loader" }] },

现在打包已经可以成功的将es6语法转化成es5语法!

4.2 解析装饰器

npm i @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators --save-dev
"plugins": [
  ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties",{"loose":true}] ]

legacy:true表示继续使用装饰器装饰器,loose为false时会采用Object.defineProperty定义属性

  • Plugin会运行在Preset之前
  • Plugin 会从第一个开始顺序执行,Preset则是相反的

4.3 polyfill

根据.browserslistrc文件,转化使用到的浏览器api

"presets": [
    ["@babel/preset-env",{ "useBuiltIns":"usage", // 按需加载 "corejs":2 // corejs 替代了以前的pollyfill }] ]

安装corejs

npm install core-js@2 --save

使用transform-runtime A plugin that enables the re-use of Babel's injected helper code to save on codesize.可以帮我们节省代码

npm install --save-dev @babel/plugin-transform-runtime @babel/runtime

.babelrc中配置插件

"plugins": [
    "@babel/plugin-transform-runtime" ]

4.4 添加eslint

安装eslint

npm install eslint
npx eslint --init # 初始化配置文件
{
    test:/\.js/,
    enforce:'pre',
    use:'eslint-loader'
},

配置eslint-loader可以实时校验js文件的正确性,pre表示在所有loader执行前执行

5.source-map

  • eval 生成代码 每个模块都被eval执行,每一个打包后的模块后面都增加了包含sourceURL
  • source-map 产生map文件
  • inline 不会生成独立的 .map文件,会以dataURL形式插入
  • cheap 忽略打包后的列信息,不使用loader中的sourcemap
  • module 没有列信息,使用loader中的sourcemap(没有列信息)
devtool:isDev?'cheap-module-eval-source-map':false

每个库中采用的sourcemap方式不一,可以根据自己的需要自行配置

6.resolve解析

想实现使用require或是import的时候,可以自动尝试添加扩展名进行匹配

resolve: {
    extensions: [".js", ".jsx", ".json", ".css", ".ts", ".tsx", ".vue"] },

7.拷贝静态文件

有些时候在打包时希望将一些静态资源文件进行拷贝,可以使用copy-webpack-plugin

安装插件

npm i copy-webpack-plugin --save-dev

使用拷贝插件

const CopyWebpackPlugin = require('copy-webpack-plugin'); new CopyWebpackPlugin([ {from:path.resolve('./src/static'),to:path.resolve('./dist')}, ])

8.配置TS环境

8.1 使用ts-loader

使用ts需要安装ts相关配置

npm install typescript ts-loader --save-dev

生成ts的配置文件

npx tsc --init

配置ts-loader

{
    test:/\.tsx?/,
    use: ['ts-loader'], exclude: /node_modules/ }

将入口文件更改成ts文件

let a:string = 'hello'; console.log(a);

执行npm run dev发现已经可以正常的解析ts文件啦!

8.2 使用 preset-typescript

不需要借助typescript

npm install @babel/preset-typescript
{
    "presets": [
       ["@babel/preset-env",{ "useBuiltIns":"usage", "corejs":2 }], "@babel/preset-react", ["@babel/preset-typescript",{ "allExtensions": true }] ], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties",{"loose":true}], "@babel/plugin-transform-runtime" ] }

9.配置ts+react环境

安装react相关模块

npm i @babel/preset-react --save-dev # 解析jsx语法
npm i react @types/react @types/react-dom react react-dom typescript
import React from 'react';
import ReactDOM from 'react-dom'; const state = {number:0}; type State = Readonly<typeof state>; class Counter extends React.Component<object,State>{ state:State = state handleClick =()=>{ this.setState({number:this.state.number+1}) } render(){ const {number} = this.state; return ( <div> <button onClick={this.handleClick}>点击</button> {number} </div> ) } } ReactDOM.render(<Counter></Counter>,document.getElementById('root'));

10.配置ts+vue环境

安装vue所需要的模块

npm install vue-loader  vue-template-compiler --save-dev
npm install vue vue-property-decorator 

配置ts-loader

{
    test: /\.tsx?/,
    use: { loader:'ts-loader', options: { appendTsSuffixTo: [/\.vue$/], }, }, exclude: /node_modules/ }

使用vue-loader插件

const VueLoaderPlugin = require('vue-loader/lib/plugin'); new VueLoaderPlugin();

配置解析.vue文件

{
    test:/\.vue$/, use:'vue-loader' }

增加vue-shims.d.ts,可以识别.vue文件

declare module '*.vue' {
    import Vue from 'vue';
    export default Vue;
}

index.tsx文件

import Vue from 'vue';
import App from './App.vue'; let vm = new Vue({ render:h=>h(App) }).$mount('#root')

App.vue文件

<template>
    <div> <div v-for="(todo,index) in todos" :key="index">{{todo}}</div> </div> </template> <script lang="ts"> import {Component,Vue} from 'vue-property-decorator'; @Component export default class Todo extends Vue{ public todos = ['香蕉','苹果','橘子'] } </script>

11.配置代理

设置服务端接口

const express = require('express'); const app = express(); app.get('/api/list', (req, res) => { res.send(['香蕉', '苹果', '橘子']); }); app.listen(4000);

安装axios获取数据

npm install axios --save-dev

配置接口请求

<template>
    <div> <div v-for="(todo,index) in todos" :key="index"> {{todo}} </div> </div> </template> <script lang="ts"> import axios from 'axios'; import {Component ,Vue} from 'vue-property-decorator'; @Component export default class Todo extends Vue{ public todos:string[] =[]; async mounted(){ let { data } = await axios.get('/api/list'); this.todos = data } } </script>

配置服务器代理路由

proxy: {
    '/api': {
    target: 'http://localhost:4000', }, }

猜你喜欢

转载自www.cnblogs.com/jianxian/p/12760375.html