webpack详解

Webpack详解

认识webpack

  • 从本质上来讲,webpack是一个现代的JavaScript应用的静态模块打包工具

image-20210813000144374

  • 打包工具:
    • grunt/gulp/webpack/rollup等
  • 前端模块化:
    • 前面的学习中提到了为什么使用前端模块化,也提到了目前使用前端模块化的一些方案:
      • AMD、CMD、CommonJS、ES6
    • 学习了webpack之后,我们就不止能使用ES6,webpack可以作为这些方式的底层支撑,并且会将其转化成浏览器能识别的代码,也能将ES6语法转为ES5的语法,处理各模块之间的依赖关系。
      • 而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当作模块来使用
  • webpack依赖于node环境
    • node环境为了支撑执行很多代码,其中必须依赖各种的包,node手动管理这些包很麻烦,所以下载node时会安装npm工具(npm packages manager)。

webpack的安装

  • 全局安装webpack(这里先指定版本号3.6.0,因为vue cli2依赖该版本)

    • npm install webpack@3.6.0 -g
  • 局部安装webpack

    • --save-dev 是开发时依赖,项目打包后不需要继续使用。

    • cd 对应目录
      npm install webpack@3.6.0 --save-dev
  • 为什么全局安装后,还需要局部安装?

    • 在终端直接执行webpack命令,使用的是全局安装的webpack
    • 当在package.json中定义了scripts脚本时,这个脚本中包含了webpack命令,那么使用的是局部webpack(详见webpack的配置)

webpack的起步

  • 新建项目 -> 创建文件夹 -> src 和 dist文件夹

    image-20210812012118963

    • dist:distribution(发布),将来直接发布这个文件夹即可。
    • src:源码

    • cd到01-webpack的起步目录下,执行

      webpack ./src/main.js ./dist/bundle.js
      • 代码使用的是CommonJS的导出导入方式,直接运行代码浏览器不会识别

      • 在index.html中,先不引用js文件,当webpack生成后再引用生成的文件即可

        <script src="./dist/bundle.js"></script>
      • 在命令中,只需引入main.js即可,会自动处理引用的文件,并将生成的文件放到dist目录下的bundle.js中

    • image-20210812020530071

    • 浏览器resource中可以看到:已经成了编译后的代码(本来浏览器可能不识别这些代码,这些编译后的代码就可以作为一个支撑,让浏览器识别)

    • 还支持同时使用不同的导出、导入方式:

      main.js

      import {name, age} from "./info";
      const {add, mul} = require('./mathUtils.js')
      console.log('Hello World');
      console.log(add(20, 30));
      console.log(mul(20, 30));
      console.log(name);
      console.log(age);

      info.js(新增,之前的mathUtils.js为CommonJS导出方式)

      export const name = 'Elian'
      export const age = 18

      重新执行package命令后,会同时打印结果:

      image-20210812021649349

webpack的配置

  • 每次执行命令webpack ./src/main.js ./dist/bundle.js很麻烦,所以对webpack进行一些配置

    • 首先,一旦当前项目准备用node中的一些东西时,就先在当前项目路径下执行一下命令进行初始化,引入依赖,在当前文件下创建包文件

      npm init
    • 终端中填写packaeg name等信息,注意中文以及符号问题

      image-20210812225107814

      回车即可

    • 这时会在当前项目下创建package.json文件(以下,文件说明)

      image-20210812230438225

    • 创建webpack.config.js

      const path = require('path') // CommonJS语法,导入path
      module.exports = { // CommonJS语法,导出
      entry: './src/main.js', // 入口
      output: { // 出口
      path: path.resolve(__dirname, 'dist'), // 动态获取绝对路径 __dirname:当前路径 dist:要生成文件的路径
      filename: 'bundle.js'
      }
      }
    • 现在只要在terminal中执行

      webpack

      即可。项目打包重新运行,输出结果

  • 当命令很长的时候,我们需要配置npm run *命令指向特定命令

    • 打开package.json文件,编辑"script"

      image-20210812231555174

      这样在执行:npm run build时,就会执行webpack命令了

    • 这样配置的原因:

      1. 可能一条命令很长,而我们要在测试时多次执行这条命令,现在无论多长的命令都可以通过npm run *的方式来运行

      2. 我们配置了全局的webpack,有时候全局的webpack可能时4以上的版本,当前项目可能使用的是3.X的版本,会找错版本导致打包之后出现问题,这时,会在本地进行安装package,之后执行npm run build 就会先在本地找webpack了。而我们要在cmd,terminal窗口中执行webpack命令,它只会在全局去找这个命令。

        在项目路径下执行

        npm install webpack@3.6.0 --save-dev

        --save-dev:是指开发时依赖,因为很多时候我们打完包以后,就不用webpack了,所以只需要安装开发时依赖即可。

    • 执行完后会在package.json中多出:

      开发时依赖

      image-20210812233032573

loader的使用

  • loader是webpack中一个非常核心的概念。
  • 已经知道webpack用来:
    • 处理js之间相关的模块引用,依赖。
    • 但是除了js还要加载css,图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。
    • 对于webpack本身的能力来说,对于这些转化是不支持的,这时给webpack扩展对应的loader就可以啦

loader使用过程

  • 步骤一:通过npm安装需要使用的loader,不同的文件需要不同的loader
  • 步骤二:在webpack.config.js中的modules关键字下面进行配置
  1. 重构代码:

    image-20210813001913839

  2. 在main.js中添加对normal.css的引用,webpack对loader的支持在

    里讲的很详细

    npm install --save-dev css-loader@2.0.2
  3. 在webpack.config.js中配置:

    const path = require('path')
    module.exports = {
    entry: './src/main.js',
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
    },
    module: { // 新增
    rules: [
    {
    test: /\.css$/,
    use: [ 'css-loader' ]
    }
    ]
    }
    }

    打开浏览器发现界面并没有变红,这里说明:css-loader只负责加载文件,不负责渲染样式,要想渲染要是还需加载一个loader

  4. 安装style-loader

    npm install style-loader@0.23.1 --save-dev

    配置webpack.config.js

    use: [ 'style-loader', 'css-loader' ]
  5. 再次运行npm run build,刷新浏览器,发现背景已经改变

  6. 注意:loader加载时是从右向左读取的

其他类型文件的loader

  • less文件:

    • 引入loader

      npm install less-loader@4.1.0 --save-dev less@3.9.0
    • 配置webpack.config.js

      rules: [
      {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
      },
      {
      test: /\.less$/,
      use: [{
      loader: 'style-loader' // creates style nodes from JS strings
      }, {
      loader: 'css-loader' // translates CSS into CommonJS
      }, {
      loader: 'less-loader' // compiles Less to CSS
      }]
      }
      ]
    • 说明:less-loader将文件加载到DOM中,less将文件转成css

      special.less

      @fontSize: 50px;
      @fontColor: orange;
      body {
      font-size: @fontSize;
      color: @fontColor;
      }

      并在main.js中引入

  • 图片:

    • 引入loader

      npm install --save-dev url-loader@1.1.2
    • 配置webpack.config.js

      rules: [
      {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
      },
      {
      test: /\.less$/,
      use: [{
      loader: 'style-loader' // creates style nodes from JS strings
      }, {
      loader: 'css-loader' // translates CSS into CommonJS
      }, {
      loader: 'less-loader' // compiles Less to CSS
      }]
      },
      {
      test: /\.(png|jpg|gif)$/,
      use: [
      {
      loader: 'url-loader',
      options: {
      limit: 14000
      }
      }
      ]
      }
      ]
    • 说明:options中limit的作用,最小的缓冲区,如果文件大于这个值,必须引入另一个file-loader,如果小于这个值,那么会将图片转成base64,这个file-loader直接下载即可,不需要针对loader进行特别的配置。

      npm install file-loader@3.0.1 --save-dev
      • 但是,之前没有loader时,并且文件大小小于配置中的大小时会转成base64的字符串形式,这个loader会将文件重新命名后加载到dist文件中,这时就要配置一个路径,让这原先的路径能找到dist下的这个文件。

      • 配置webpack.config.js中的output

        image-20210813225740513

      • 添加publicPath路径,这个属性的目的就是为了在url前面加上一个对应的路径。

      • 同时,dist下生成的文件是个hash32位的命名,很乱,不知道原图片是什么,这个时候还需对名字进行配置:

        image-20210813231813626

        • 最终生成的文件:

          image-20210813232141422

  • ES6转ES5的babel的loader:

    npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

    配置webpack.config.js

    image-20210813234502496

webpack中配置Vue

引入vue.js

  • 后续项目中我们会使用Vuejs进行开发,而且会以特殊的文件来组织vue的组件

  • 安装依赖

    • 因为后续发布的时候也会使用vue,所以并不是开发时依赖

      npm install vue --save
  • 然后就可以按照之前的方式使用vue了。

    // index.html中添加vue挂载的div
    <div id="app">
    {{message}}
    </div>
    // main.js中引入vue,并copy我们之前的代码过来
    import vue from 'vue';
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello World'
    }
    })
    • build运行之后发现报错了

      image-20210814003015388

      这是因为:

      vue在构建最终的发布版的时候,构建了两类发布版本:

      1. runtime-only --在这个版本中不允许有template
      2. runtime-compiler --在这个版本中允许有template,因为有compiler代码用于编译template
    • 当前解决办法

      • 在webpack.config.js中添加resolve:

        image-20210814004039127

el和template的区别:

  • 正常运行以后,我们来考虑一个问题:

    • 如果我们希望将data中的数据显示在界面中,就必须修改index.html
    • 如果我们后面自定义了组件,也必须修改index.html来使用组件
    • 但是vue作为单页面复用技术,并须希望手动的来频繁的修改,是否可以做到呢?
  • 定义template属性:

    • 当index.xml中和Vue实例中同时定义了同一个div,就会将template中的div替换到html中的div去

      image-20210814005726962

对template进行优化

  • 第一次优化:

    image-20210814011750300

    抽取模板

    在vue中定义components

    在vue的template中引用

  • 第二次优化:

    image-20210814013327017

    在src目录下新建vue文件夹,创建app.js并导出一个default的模板

    定义components,引入App

    在main.js中导入,并在template中引用

  • 第三次优化

    • 在vue目录下创建vue componen文件

      <template>
      <div id="app">
      <h2 class="message">{{message}}</h2>
      <button @click="btnClick">按钮</button>
      <h2>{{name}}</h2>
      </div>
      </template>
      <script>
      export default {
      name: "App",
      data() {
      return {
      message: 'Hello World',
      name: 'Elian'
      }
      },
      methods: {
      btnClick() {
      console.log('按钮被点击了');
      }
      }
      }
      </script>
      <style scoped>
      .message {
      color: orange;
      }
      </style>
    • 删除刚刚创建的app.js即可,并在main.js中引用即可

      import Vue from 'vue';
      import App from './vue/app'; // 新的引用
      new Vue({
      el: '#app',
      template: `<App/>`,
      components: {
      App
      }
      })

      这时进行build会报错,原因是没有处理.vue的loader

  • 安装loader(开发必用

    • 安装:vue-loader 和 vue-tempalte-compiler[vue-loader最好用13.0.0以上的版本,避免安装其他插件]

      npm install --save-dev vue-loader vue-template-compiler
    • 配置:

      {
      test: /\.vue$/,
      use: ['vue-loader']
      }
    • vue-loader在14以上的版本中会出现需要另外安装插件的问题

      image-20210814021811792

plugin的使用

认识plugin

  • plugin是什么?
    • plugin(插件),通常是对现有 的架构进行扩展
    • webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等。
  • loader和plugin的区别?
    • loader主要用于转换某些类型的模块,他是一个转换器
    • plugin是插件,它是对webpack本身的扩展,是一个扩展器
  • plugin的使用过程:
    • 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
    • 步骤二:在webpack.config.js中的plugins中配置插件

让webpack更加好用的plugins

  • 添加版权的Plugin

    • 这个插件属于内置插件,不需要再install,直接进行引用,并添加plugins属性并添加要输入版权的名称

      image-20210814133800070

  • 打包html的plugin

    • 目前我们的index.html文件是存放在项目的根目录下的。

      • 我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。
      • 所以我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
    • HtmlWebpackPlugin插件可以为我们:

      • 自动生成一个index.html文件(可以指定模板来生成)
      • 将打包的js文件,自动通过script标签插入到body中
    • 安装HtmlWebpackPlugin插件

      npm install html-webpack-plugin --save-dev
    • 使用插件,修改webpack.config.js文件中plugins部分的内容如下:

      • 这里的template表示根据什么模板来生成index.html

      • 另外,我们休要删除之前在output中添加的publicPath属性删除,否则插入的script标签中的src可能会有问题。

        image-20210814174700550

      • index.html中,还是需要一个id为app的div,但是不用再引入bundle.js了,会自动引入dist下的bundle.js

  • js压缩的Plugin

    • 在项目发布之前,我们必然需要对js等文件进行压缩处理

      • 这里,我们就对打包的js文件进行压缩

      • 我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致

        npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
    • 修改webpack.config.js文件,使用插件:

      const uglifyJsPlugin = require('uglifyjs-webpack-plugin')
      ----
      plugins: [
      ...
      new uglifyJsPlugin()
      ]

搭建本地服务器

  • webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。

  • 不过它是一个单独的模块,在webpack中使用之前要先安装他:

    npm install --save-dev webpack-dev-server@2.9.1
  • devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:

    • cnotentBase:为那一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
    • port:端口号
    • inline:页面实时刷新
    • historyApiFallback:在SPA页面中,依赖HTML5的history模式
  • webpack.config.js文件和package.json配置如下:

    image-20210814222806634

  • 执行

    npm run serve

抽离webpack.config.js

  • 为什么要抽离?

    • 像我们最后一个配置devServer,只有在开发的时候才用到,真正发布项目后,是不会使用这个devServer的,这时我们就要配置一个开发时运行的和一个发布时运行的。

      1. 项目下创建src的同级目录build文件夹

      2. 安装webpack-merge

        npm install webpack-merge@4.1.5 --save-dev
      3. 创建base.config.js、dev.config.js、prod.config.js文件,将公共的配置放在base.config.js中,而开发时的配置放在dev.config.js中,生产时的配置文件放在prod.config.js中。

        base.config.js

        const path = require('path')
        const webpack = require('webpack')
        const HtmlWebpackPlugin = require('html-webpack-plugin')
        module.exports = {
        entry: './src/main.js',
        output: {
        path: path.resolve(__dirname, '../dist'), // 修改路径为../dist
        filename: 'bundle.js'
        },
        module: {
        rules: [
        {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
        },
        {
        test: /\.less$/,
        use: [{
        loader: 'style-loader' // creates style nodes from JS strings
        }, {
        loader: 'css-loader' // translates CSS into CommonJS
        }, {
        loader: 'less-loader' // compiles Less to CSS
        }]
        },
        {
        test: /\.(png|jpg|gif)$/,
        use: [
        {
        loader: 'url-loader',
        options: {
        limit: 1400,
        name: 'img/[name].[hash:8].[ext]'
        }
        }
        ]
        },
        {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
        loader: 'babel-loader',
        options: {
        presets: ['es2015']
        }
        }
        },
        {
        test: /\.vue$/,
        use: ['vue-loader']
        }
        ]
        },
        resolve: {
        extensions: ['.js','.css','.vue'],
        alias: {
        'vue$': 'vue/dist/vue.esm.js'
        }
        },
        plugins: [
        new webpack.BannerPlugin('最终版权归Elian所有'),
        new HtmlWebpackPlugin({
        template: `index.html`
        })
        ]
        }

        dev.config.js

        const baseConfig = require('./base.config')
        const webpackMerge = require('webpack-merge')
        module.exports = webpackMerge(baseConfig, {
        devServer: {
        contentBase: '../dist', // 要监听的文件夹
        inline: true // 实时监听
        }
        })

        prod.config.js

        const baseConfig = require('./base.config')
        const uglifyJsPlugin = require('uglifyjs-webpack-plugin');
        const webpackMerge = require('webpack-merge')
        module.exports = webpackMerge(baseConfig, {
        plugins: [
        new uglifyJsPlugin()
        ]
        })
      4. 修改base.config.js下的path路径为../dist,修改package.json文件,添加build,修改serve

        image-20210814232655570

总结:

创建一个简单的模板:

  1. 目录结构:

    image-20210815001715634

  2. base.config.js代码:

    const path = require('path') // node语法,导入path
    const webpack = require('webpack')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    module.exports = { // CommonJS语法,导出
    entry: './src/main',
    output: {
    path: path.resolve(__dirname, '../dist'), // 动态获取绝对路径 __dirname:当前路径 dist:要生成文件的路径
    filename: 'bundle.js'
    /*指定文件去dist根下找,如果把index.html放到dist下,就是绝对路径
    * 不需要配置这个了*/
    /*publicPath: 'dist/'*/
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ['style-loader', 'css-loader']
    },
    {
    test: /\.less$/,
    use: [{
    loader: 'style-loader' // creates style nodes from JS strings
    }, {
    loader: 'css-loader' // translates CSS into CommonJS
    }, {
    loader: 'less-loader' // compiles Less to CSS
    }]
    },
    {
    test: /\.(png|jpg|gif)$/,
    use: [
    {
    loader: 'url-loader',
    options: {
    // 当加载的图片,小于limit时,会将图片编译成base64字符串形式
    // 当加载的图片,大于limit时,需要使用file-loader模块进行加载
    limit: 1400,
    // [name]:原名 [hash:8]取hash8位 [ext]继承原格式
    name: 'img/[name].[hash:8].[ext]'
    }
    }
    ]
    },
    {
    test: /\.js$/,
    // 排除node_modules等文件夹下的文件转换
    exclude: /(node_modules|bower_components)/,
    use: {
    loader: 'babel-loader',
    options: {
    presets: ['es2015']
    }
    }
    },
    {
    test: /\.vue$/,
    use: ['vue-loader']
    }
    ]
    },
    // 目的是为了在执行 import Vue from 'vue' 时
    // 先来看有没有将vue指向别的文件
    // 这时就不会去找默认的文件了,而是来找这个指定的文件
    resolve: {
    extensions: ['.js','.css','.vue'],
    alias: {
    'vue$': 'vue/dist/vue.esm.js'
    }
    },
    plugins: [
    new webpack.BannerPlugin('最终版权归Elian所有'),
    new HtmlWebpackPlugin({
    template: `index.html`
    })
    ]
    }
  3. dev.config.js代码

    const baseConfig = require('./base.config')
    const webpackMerge = require('webpack-merge')
    module.exports = webpackMerge(baseConfig, {
    devServer: {
    contentBase: '../dist', // 要监听的文件夹
    inline: true // 实时监听
    }
    })
  4. prod.config.js代码

    const baseConfig = require('./base.config')
    const uglifyJsPlugin = require('uglifyjs-webpack-plugin');
    const webpackMerge = require('webpack-merge')
    module.exports = webpackMerge(baseConfig, {
    plugins: [
    new uglifyJsPlugin()
    ]
    })
  5. index.html模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
    <body>
    <div id="app">
    </div>
    </body>
    </html>
  6. App.vue代码(可以等完成npm install之后再创建)

    <template>
    <div id="app">
    <h2 class="message">{{message}}</h2>
    <button @click="btnClick">按钮</button>
    <h2>{{name}}</h2>
    <h2>{{name}}</h2>
    </div>
    </template>
    <script>
    export default {
    name: "App",
    data() {
    return {
    message: 'Hello World',
    name: 'Elian'
    }
    },
    methods: {
    btnClick() {
    console.log('按钮被点击了');
    }
    }
    }
    </script>
    <style scoped>
    .message {
    color: orange;
    }
    </style>
  7. main.js代码

    //使用vue进行开发
    import Vue from 'vue';
    import App from './vue/App.vue'
    new Vue({
    el: '#app',
    template: `<App/>`,
    components: {
    App
    }
    })
  8. package.json代码

    {
    "name": "template",
    "version": "1.0.0",
    "description": "vue project develop template",
    "main": "webpack.config.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js",
    "webpack": "webpack --version"
    },
    "author": "Elian",
    "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-es2015": "^6.24.1",
    "css-loader": "^2.0.2",
    "file-loader": "^3.0.1",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "style-loader": "^0.23.1",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^1.1.2",
    "vue-loader": "^13.0.0",
    "vue-template-compiler": "^2.5.21",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.5"
    },
    "dependencies": {
    "vue": "^2.5.21"
    }
    }
  9. 所有配置完以后,在当前目录下cmd执行以下命令

    npm init
    npm install --legacy-peer-deps
    然后执行步骤6
    npm run dev/build
posted @   coderElian  阅读(118)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示