zoukankan      html  css  js  c++  java
  • 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',
        },
    }
  • 相关阅读:
    CF140CNew Year Snowmen
    ZR10.1青岛集训三地联考
    CF1228——记一次和紫名失之交臂的CF
    CF1220
    codeforce 382 div2 E —— 树状dp
    codeforce 381 div2
    codeforce 380(div.2)
    Bishops Alliance—— 最大上升子序列
    codeforce 379(div.2)
    模板——BigInteger
  • 原文地址:https://www.cnblogs.com/fightjianxian/p/12760375.html
Copyright © 2011-2022 走看看