webpack-从零搭建vuecli环境
阅读原文时间:2023年07月08日阅读:1

// 1最早期就只是html和css处理网页

// 2发明一种语言来操作html和css js

// 3早期只是在html文件里直接在script标签里写一些脚本代码

// 4随着Ajax的出现,慢慢形成了前后端的分离

// 5客户端需要完成的事情越来愈多,代码量与日俱增

// 6为了应对代码量的增加,通常会将代码组织在多个js文件中,进行维护,就会出现许多问题

首先,当我们编写代码时,不通过模块化的思想想要引入一个js,通常是在html文件中创建一个script标签,引入我们需要的js,如果我们引入的是自己手写的js,在过个js文件中,或者多人合作开发时候,很容易发生变量重名的冲突。

// 变量命名冲突
// a.js  小明开发的
var a = true;

// b.js  小红开发的
var a = false

// index.html 中同时引入a.js、b.js后,小明记得自己的a变量是true

//c.js //小明继续开发
if(a){
      console.log('我定义的a变量是true')
}
这样就报错了,小明不知道自己的变量被修改了,所以模块化思想之前很容易造成变量命名冲突的问题

所以必须解决这种问题,通过一些方法去避免这些错误,比如:匿名函数,但匿名函数又会发生每个js文件中的作用域私有,而代码不可发生复用的问题,因此又需要想方法解决,例如:

// a.js
var moudleA = (function() {
  const obj = {}
  const a = '我是模块A中的变量a'

  obj.a = a
  return obj // 将匿名函数中的变量暴露出去,实现代码复用
})()

// b.js
(function() {
  console.log(moudleA.a) // 引用a中的变量,并且解决变量重名
})()

// index.html 中同时引入a.js、b.js
并且,这种导入方式对js插入顺序依赖性很强,一般公司多人开发,不同的开发人员引入不同的js文件,插入位置错误发生的报错。
但不可否认,通过匿名函数将变量暴露出去这种方式,就是最基础的模块封装。当然现在对于前端模块化已经有了很多规范:Common.js、AMD、CMD、ES6中的Modules。
总结:
0代码复杂化带来的问题
1为什么需要模块化
2模块化方案
3es6之前的方案
4处理模块依赖,整合打包
5不仅仅能处理js,其他html,css,图片,json也可以模块化处理

简单通俗的说webpack就是一个前端自动化工具 模块 打包

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

它是依赖于node环境的

官网:

https://www.webpackjs.com/concepts/

1先安装node环境

node -v npm -v 查看版本

2新建文件夹vuecli,并初始化

npm init -y

文件夹是中文名的话,这样会报错,需要npm init回车然后填写名称,其他信息一路回车即可,然后熟悉的package.json两兄弟文件就在根目录中出来了

npm安装是缩写模式, 简单说下:

-D 为 --save-dev 开发环境中使用

-S 为 --save 生产环境中使用

-g 为 --global 全局安装

安装npm i webpack webpack-cli -D 我之前已经安装过全局的了

为啥在项目中还要安装本地webpack,而不是直接用全局的?

当你全局安装了webpack, 你使用指令webpack打包又先调用的是你全局的webpack。 毕竟你今天安装的webpack是这个版本,不能保证以后谁拿你代码一运行调用他电脑下载的webpack版本,beng,满屏飘红就出来了。因为当时做项目时的webpack版本不一样,当你克隆下来时,你就会把当时的webpack版本也克隆下来,再安装时候,就不会出现版本冲突了 所以保险点,项目都安装自己的webpack,版本不会冲突。

3测试webpack打包使用

新建文件夹dist和文件mian.js和index.html

index.html文件引入打包后的文件 命名为bundle.js如图

maian.js输入测试代码

在终端输入命名 版本不同命令输入也不同 注意

webpack main.js -o dist/bundle.js 会生成如图

测试代码也能成功

注意:此时的webpack命令是调用全局的webpack

当你安装好webpack后执行打包指令后, 执行"webpack xxxx"时要注意,此时如果你安装了全局webpack,任然调用的是你全局的而不是项目自己的,你项目调用自己的webpack指令应该是./node_modules/.bin/webpack xxxxx

为了解决该问题,我们可以添加webpack.config.js

4添加webpack.config.js文件

// webpack.config.js
const path = require("path")  //node自带模块 引入路径模块
module.exports = {
  entry:  "./main.js",  // 入口
  output: {
    path: path.resolve(__dirname, 'dist'), // 出口。必须为绝对路径,所以借助node的path模块 __dirname保存的是当前文件的路径
    filename: 'bundle.js' // 打包后文件名
  },
}
// 意思就是:我根目录有个main.js文件,里面导入了好多东西,帮我打包到我目录里面的dist文件下bundle.js文件里


// package.json
并且还需要更改一下package.json文件中的scripts属性
{
...
  scripts: {
    ...
    "build": "webpack"  // build可以随便取,如: 叫xxx,终端运行 npm run xxx即可
                        //执行脚本"build": "webpack"时候,它会优先找本地的webpack,
                        //只要实在终端直接输入webpack打包都是使用的全局的,所以为了解决这个问题,用脚本这样会优先找本地的
  }
...
}

// 意思: 以后我直接在终端运行一个叫build的指令去调用webpack打包, 并且通过该指令, 会自动帮我们调用开发环境中的webpack打包

当运行npm run build时,webpack会看看我们根目录是不是有一个叫webpack.config.js的文件,然后读取运行其中的配置。

弄完这一步,最后将index.html中引入的main.js改为bundle.js后,我们可以愉快的将各个js文件当成模块,各种require、import、、、,尽情导入导出

比如,我这里在src文件夹中新建js文件夹,添加info.js、mainUnit.js然后再main.js中引入并使用

webpack并不能识别许多文件类型,我们需要通过各种各样的loader,来帮webpack识别。

接下来做css相关,图片相关的loader,新建文件如下

在main.js中引入 require("xxx.css")或者import "xxx.less",然后一运行报错

5引入css和less相关loader

进入webpack官网, 点击导航中的LOADERS,点击左边导航的样式,然后就可以根据需求对照文档安装和配置你需要的样式相关loader了

我这里安装css和less相关的loader,npm i style-loader css-loader less-loader less -D 因为loader基本用于开发环境, 所以安装一般都为--save-dev

// webpack.config.js
module.exports = {
  ...
 module:{
  rules: [
      {
        test: /\.css$/,
        // css-loader 只负责解析css,并不负责插入样式到页面
        // style-loader 负责将样式插入页面中
        // 使用多个loader时,从右往左调用
        use: ['style-loader','css-loader']
      },
      {
        test: /\.less$/,
        use: [{ // use中如需配置其他options 可以使用Object形式, 否则不配置可直接用Array形式,同上css-loader
          loader: "style-loader"
        }, {
          loader: "css-loader"
        }, {
          loader: "less-loader"
        }]
      },
  ]
 }
 ...
}

5引入图片相关loader

在网上随便找2张一大一小图片

npm install --save-dev url-loader

// webpack.config.js
module.exports = {
  ...
  rules: [
    ...
    {
        test: /\.(png|jpg|gif)$/,
        use: [{
          loader: 'url-loader',
          options: {
            // 当加载图片,小于limit时,会将图片编译成base64字符串形式
            // 当加载图片,大于limit时,需要安装file-loader进行加载,加载图片会放入你打包输出的文件目录
            limit: 8192 // 8192 / 1024 = xKB
          }
        }]
      }
    ...
  ]
 ...
}

limit属性说明如上方注释,当图片小于limit设定的值时以base64字符串形式插入在打包的js中,此时没问题。

但是,一旦大于设定值,webpack会报错,需要安装file-loader,安装即可,不需要单独配置。

然后wenpack会将引入图片完整打包放入dist文件夹目录下,名称为32位hash值,并将开发环境中引用的图片路径替换为打包生成的图片,而此时index.html引用的为开发目录中(即img文件夹下)的图片,所以打包后再运行index.html发现背景图片不见了

所以这时我们需要当图片大于limit设定值时的打包后页面引入路径更改为dist/xxxxx,好的,进入webpack.config.js,在其中output属性中添加publicPath属性

npm install --save-dev file-loader

// webpack.config.js
module.exports = {
...
output: {
    ...
    // 添加该属性,以后打包文件所有涉及到url的东西,都会自动在路径前添加 dist/
    publicPath: 'dist/'
  }
...
}

我们通常并不希望,所有的img全部打包在dist文件夹下,而是希望放在dist/img文件夹中,并且希望图片保持原名,但怕重名,还需要加点hash值。

因此,我们可以继续配置url-loader规则

// webpack.config.js
module.exports = {
...
rules: [
  ...
  {
        test: /\.(png|jpg|gif)$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 8192 ,
            // 当不希望它打包直接放入dist文件中时,可以添加name属性,
            // 当需要保存原名时可以添加[name]
            // 当需要防止重名又不需要太长的hash值时可以添加[hash: x] x为你需要hash值位数
            name: 'img/[name].[hash:8].[ext]'
          }
        }]
      }
  ...
]
...
}
因为之前有publicPath配置,所以打包运行index.html页面图片引入路径仍然为dist/img/xxxx.xxx

7ES6转ES5

完成上面一步之后,领导打开他很久没更新的浏览器,发现页面并没有出来,打开控制台一看,全是ES6语法报错,然后立马说:“你这打包不对劲啊,浏览器版本一低就各种报错,我们不能保证用户都使用可以识别ES6语法的浏览器,代码应该要将老旧浏览器都兼容啊。”

好吧,满足他的需求,打开浏览器搜索babel,进入中文官网,点击设置选择Webpack,对照文档在编辑器中一顿输出。

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

安装完成后,在webpack.config.js中添加规则:

// webpack.config.js
module.exports = {
  ...
  rules: [
    ...
      {
        test: /\.js$/,
        exclude: /node_modules/, // 排除的目录
        // 使用babel-loader将ES6代码转为ES5,做浏览器兼容
        // 同时需要建立.babelrc文件,调用@babel/preset-env插件将E6转为E5S
        loader: "babel-loader"
      }
    ...
  ]
 ...
}

此时,babel-loader已经可以将ES6语法识别,但是打包将ES6转ES5还需要@babel/preset-env插件,所以我们要新建一个名为.babelrc的babel配置文件使用该转译插件:

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

9使用Vue

正准备执行回家程序操作,领导叫住说:“项目前端框架要用vue,就用你那玩意搞,正好可以打包。”得,继续搭vue吧。

npm i vue -S

因为vue不仅是在开发环境中使用,并且打包后依然需要依赖vue,所有安装在生成环境中。

安装之后,在main.js中使用一下吧

// main.js
import Vue from "vue";
new Vue({
  el: "#app",
  data: {
    msg: "哈哈,使用了Vue"
  }
})
// 然后在index.html中使用一下
// index.html
...
<div id="app">
  <h2>{{ msg }}</h2>
</div>
...

好吧,main.js中引入的vue模块,使用的是runtime-only版本的,该版本有vue运行代码,但没有编译template的代码。

那咋办,换个vue模块引入的版本呗,打开webpack.config.js。

// webpack.config.js
module.exports = {
  ...
  // 设置模块如何被解析
  resolve: {
    // 当安装vue时,默认使用的是runtime-only版本,此版本只含有vue运行的代码,不包含编译template的代码
    // 需要重新更换含有runtime-compiler的版本,因为runtime-complier含有complier代码可以用于编译template
    // alias(别名): 用别名代替前面的路径,不是省略,而是用别名代替前面的长路径
    // 如下,当main.js中import Vue from "vue"时,因为vue是别名,所以实际为import Vue from "vue/dist/vue.esm.js"
    // 别名好处是webpack直接会去别名对应的目录去查找模块,减少了webpack自己去按目录查找模块的时间
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
 ...
}

属性解释如上图,语文好的去官方文档看,反正我是理解不了它说的啥,文档说点白话,通俗易懂不好吗。

简单说alias就是拼接导入模块的路径,emmm,还是举例吧,如下:

// webpack.config.js
...
alias: {
  'vue$': 'vue/dist/vue.esm.js'  // 这是别名vue
}
...

// main.js
import Vue from "vue"  <==> import Vue from "vue/dist/vue.esm.js"

import Vue from "vue/xxx/xxx"  <==> import Vue from "vue/dist/vue.esm.js/xxx/xxx"

好了,应该懂了,反正也是写我自己看的,你们不理解百度去吧。

在我这其实是将alias当成vue模块引入版本重定向,当引入vue,默认引入vue.runtime.common.js文件,而我将引入文件重定向为vue.esm.js。

我怎么知道默认引入哪个版本的?

打开node_modules/vue/package.json,查看其中main属性,就是vue模块默认引入的版本。

其他版本,在node_modules/vue/dist文件夹中。

到这一步,vue配置就完成了。

10vue中template的封装

注意代码的演变

代码抽离1

// main.js
import Vue from "vue";
new Vue({
  el: "#app",
  template:`<div>{{msg}}</div>`
  data: {
    msg: "哈哈,使用了Vue"
  }
})
// 然后在index.html中使用一下
// index.html
...
<div id="app">
</div>
...

代码抽离二

 main.js
const App = {
     template: `<div>{{msg}}</div>`,
    data() {
        return {
            msg: "哈哈,使用了Vue123"
        }
     }
 }
import Vue from "vue";
new Vue({
  el: "#app",
  template:`<App></App>`,
  components: {
        App
    }
})
// 然后在index.html中使用一下
// index.html
...
<div id="app">
</div>
...

代码抽离三

 main.js
import App from './app.vue'
import Vue from "vue";
new Vue({
  el: "#app",
  template:`<App></App>`,
  components: {
        App
    }
})
// 然后在index.html中使用一下
// index.html
...
<div id="app">
</div>
...
// app.vue
<template>
  <div class='app'>
    <div>{{msg}}</div>
  </div>
</template>
<script>
export default {
  name: 'app',
  data() {
      return {
    msg: "哈哈,使用了Vue1235"
      }
  },
  mounted() { },
  watch: {},
  computed: {},
  methods: {},
  components: {}
};
</script>
<style lang='less' scoped='scoped'>
</style>

注意 会遇到两个问题

第一

使用vue文件需要安装vue-loader

cnpm i vue-loader vue-template-compiler -D

第二

vue-loader版本问题

错误是指vue-loader在14版本以上,需要安装另外的插件

方法一:

进入package.json文件中,找到vue-loader的版本

^13.0.0指会自动匹配13.x.x中的最新版本,但是不会匹配到14.0.0

和^(插入符号)一对还有一个符号~(波浪符号)

~13.3.0指会匹配更新到13.3.x的最新版本,但不会更新到13.4.0

然后npm install

方法二:

在webpack.config.js文件中配置以下

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

plugins: [
    new VueLoaderPlugin()
]

之后就可以使用vue组件 路由 进行基本的开发了