复盘前端工程化

274 阅读13分钟

1 babel

这篇文章主要收录了babel工作原理,webpack中的loader、plugin、devServer、resolve、打包,Source Map,Vite,Element ui等等相关知识

1.1 babel

babel是一个工具链,依赖各种插件es6+ 的语法转化为低版本(浏览器可识别)的语法

当然,如果一个个插件都要安装,那就太麻烦了,这时会用到预设

preset

预设可以将常用的插件集成到一起

可以安装 @babel/preset-env 使用预设

1.2 babel-loader

webpack是模块化打包工具,并不会帮我们将es6的代码转化为es5,这个转化工作由babel-loader(依赖babel)完成,当然,还需要使用相关的插件~(比如转化const、let的插件,转化箭头函数的插件等等)。当然,可以使用预设

1.3 babel的配置文件

可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:

  1. babel.config.json(或.js、.cjs、.mjs)文件,推荐
  2. .babelrc.json(或babelrc,.js ,.cjs,.mjs)文件

比如 babel.config.js

module.exports = {
    presets: [
        '@babel/preset-env'
    ]
}

1.4 底层原理

你可能想问:babel如何将(es6、ts、react)转换成es5的呢?

其实,将源代码(原生语言)转化成另一种源代码(目标语言),这是编译器的职责

babel就类似于一个编译器

工作流程

babel也拥有编译器的工作流程:

  1. 解析(Parsing)
  2. 转化(Transformation)
  3. 生成(Code Generation)

可以去 github.com/jamiebuilds… 查看详细过程

原生代码 -> 词法分析 -> 数组 -> 语法分析 -> AST -> 遍历 -> 访问 -> 应用插件 -> 新的AST -> 目标代码

node.js中通过babel体验es6模块化

babel语法转换插件,可以把高级的、有兼容性的js代码转换成低级的、没有兼容性的代码

  1. npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
  2. npm install --save @babel/polyfill
  3. 项目根目录创建文件babel.config.js
  4. babel.config.js 文件内容如下
const presets = [
	["@babel/env", {
		targets: {
			edge: "17",
			firefox: "60",
			chrome: "67",
			safari: "11.1"
		}
	}]
];
module.exports = { presets };

5.通过 npx babel-node index.js 执行代码(高版本npm自带npx)

2 webpack

2.1 当前web开发面临的困境

  • 文件依赖关系错综复杂
  • 静态资源请求效率低
  • 模块化支持不友好
  • 浏览器对高级javascript特性兼容程度较低

2.2 webpack概述

webpack是一个流行的前端项目构建工具(打包工具),可以解决当前web开发中所面临的的困境。

webpack提供了友好的模块化支持,以及代码压缩混淆处理js兼容性问题性能优化等强大功能,从而让程序员把工作的重心放到具体的功能实现上,提高开发效率和项目的可维护性。

image-20211009145904380.png

grunt/gulp和webpack有什么不同?

grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。

而webpack更加强调模块化开发管理,而文件压缩合并,预处理等功能,是他附带的的功能。

2.3 webpack的基本使用

安装webpack首先要安装node.js,node.js自带了软件包管理工具npm

在项目中安装和配置webpack

  1. 运行 npm install webpack webpack-cli -D 命令,安装webpack相关的包
  2. 在根目录中,创建名为 webpack.config.js 的 webpack配置文件
  3. 在webpack 的配置文件中,初始化如下基本配置:
module.exports = {
	mode: 'development'//mode 用来指定构建模式还有 production
}

4.在package.json 配置文件中的scripts 节点下,新增dev 脚本如下:

"scripts": {
	"serve": "webpack"//scripts节点下的脚本,可以通过npm run 执行
}

5.在终端中运行 npm run serve 命令,启动webpack 进行项目打包。

development模式下的main.js(还未压缩)

image-20211009155043548.png

image-20211009154512190.png

production模式下的main.js

image-20211009155111945.png

image-20211009154926508.png

开发时使用development模式,提高编译速度,

发布时使用production模式。

配置打包的出口与入口

webpack的4.x版本中默认约定:

  • 打包的入口文件为 src -> index.js
  • 打包的输出文件为 dist -> main.js

如果要修改打包入口与出口,可以在 webpack.config.js 中新增配置信息:

const path = require('path')//导入node.js中专门操作路径的模块
module.exports = {
	entry: path.join(__dirname, './src/index.js'),//打包入口文件的路径
	output: {
		path: path.join(__dirname,'./dist'),//输出文件的存放路径
		filename: 'bundle.js'//输出文件名称
	}
}

2.4 webpack中的loader

通过loader打包非js模块

实际开发中,webpack默认只能打包处理以.js后缀名结尾的模块,其它非.js后缀名结尾的模块,webpack默认处理不了,需要调用loader加载器才可以正常打包,否则会报错!

loader加载器可以协助webpack打包处理特定的文件模块,比如:

  • less-loader 可以打包处理 .less相关的文件
  • sass-loader 可以打包处理 .sass相关的文件
  • url-loader 可以打包处理 css 中与 url路径相关的文件

loader的调用过程

image-20211010094852881.png

webpack中加载器的基本使用

打包处理css文件

css-loader只负责加载

还需要style-loader将样式添加到DOM中

style-loader

  1. 运行 npm i style-loader css-loader -D 命令,安装处理css文件的loader
  2. 在 webpack.config.js 的module -> rules 数组中,添加loader规则如下:
  3. // 所有第三方文件模块的匹配规则
    module: {
    	rules: [
    		{ test: /.css$/, use: [ 'style-loader', 'css-loader' ] }
    	]
    }
    

其中,test表示匹配的文件类型(正则表达式),use表示对应要调用的loader

注意:

  • use 数组中指定的loader顺序是固定的
  • 多个loader的调用顺序是:从后往前调用

打包处理less文件

  1. 运行 npm i less-loader less -D 命令,安装处理less文件的loader
  2. 在 webpack.config.js 的module -> rules 数组中,添加loader规则如下:
  3. // 所有第三方文件模块的匹配规则
    module: {
    	rules: [
    		{ test: /.less$/, use: [ 'style-loader', 'css-loader', 'less-loader' ] }
    	]
    }
    

打包处理scss文件

  1. 运行 npm i sass-loader node-sass -D 命令,安装处理sass文件的loader
  2. 在 webpack.config.js 的module -> rules 数组中,添加loader规则如下:
  3. // 所有第三方文件模块的匹配规则
    module: {
    	rules: [
    		{ test: /.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }
    	]
    }
    

配置postCSS自动添加css的兼容前缀

  1. 运行 npm i postcss-loader autoprefixer -D 命令
  2. 在项目根目录中创建postcss的配置文件postcss.config.js,并初始化如下配置:
  3. const autoprefixer = require('autoprefixer')//导入自动添加前缀的插件
    module.exports = {
    	plugins: [ autoprefixer ]//挂载插件
    }
    
  4. 在 webpack.config.js 的module -> rules 数组中,修改css的loader规则如下:
  5. // 所有第三方文件模块的匹配规则
    module: {
    	rules: [
    		{ test: /.css$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }
    	]
    }
    

打包样式表中的图片和字体文件

  1. 运行 npm i url-loader file-loader -D 命令
  2. 在 webpack.config.js 的module -> rules 数组中,添加loader规则如下:
  3. // 所有第三方文件模块的匹配规则
    module: {
    	rules: [
    		{ 
                test: /.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
                use: [
                  {
                    loader: 'url-loader',
                    options: {
                      limit: 90 * 1024
                    }
                  }
                ]
          	}
    	]
    }
    

当加载的图片,小于limit时,会将图片编译成base64字符串形式。

当加载的图片,大于limit时,需要使用file-loader模块进行加载。

然而,还没结束。

去浏览器检查时,并没有显示图片,打开控制台发现图片路径错了

解决方法:在webpack.config.js里添加一个output的配置,使用共用路径

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: 'dist/'
}

还有一个问题:打包之后图片名字变了

解决方法:在webpack.config.js里的module里的file-loader添加一个option

options: {
  limit: 90 * 1024,
  name: 'img/[name].[hash:8].[ext]'
}

打包处理js文件中的高级语法

  1. 安装babel转换器相关的包:npm install -D babel-loader @babel/core @babel/preset-env
  2. 在webpack.config.js的module -> rules数组中,添加loader规则如下:
  3. // exclude为排除项,表示babel-loader不需要处理node_modules中的js文件
    { 
        test: /.js$/, 
        exclude: /(node_modules|bower_components)/,
        use: {
            loader: 'babel-loader', 
            options: {
                presets: ['@babel/preset-env']
            }
        }
    }
    

资源模块类型

webpack5开始,我们就可以直接使用资源模块类型asset module type),来替代某些loader

  • asset/resource 发送一个单独的文件并导出URL,替代file-loader
  • asset/inline 导出一个资源的data URI,替代url-loader
  • asset/source 导出资源的源代码。之前替代raw-loader

打包处理vue文件

安装 npm install vue-loader vue-template-compiler -d相关的包

配置

{ 
  test: /.vue$/,
  use: ['vue-loader']
}
runtime-compiler和runtime-only区别(掌握)

runtime-compiler

可以解析template

template->ast->render->vdom->ui

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    el: '#app',
    template: '<App/>',
    components: {
        App
    }
})

runtime-only(性能更高,代码量更少)

不解析template

render->vdom->ui

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    el: '#app',
    render: h => h(App)
})

那么,.vue文件的template是由谁处理的呢?

vue-template-compiler,.vue文件的template已经被转换成render函数了

render函数的完整写法

h只是代号

render: function (createElement) {
    // 1.普通用法createElement('标签',{标签的属性},['标签内容'])
    return createElement('h2',{class: 'box'},['hello'])
    // 2.也可以传入组件对象createElement(App)
}
vue不同版本的含义
vue(.runtime).global.(.prod).jsvue(.runtime).esm-browser.(.prod).jsvue(.runtime).esm-bundler.jsvue.cjs.(.prod).js
通过使用用于原生ES模块导入使用用于webpack、rollup、parcel等构建工具服务器端渲染使用
会暴露一个全局的Vue来使用通过使用构建工具默认的是vue.runtime.esm-bundler.js通过require()在node中使用
如果需要解析template模板,需要手动指定vue.esm-bundler.js
vue中编写DOM元素

vue开发过程中有三种方式来编写DOM元素:

  • template,通过源码中一部分代码对其进行编译
  • render函数(h),直接返回一个虚拟节点
  • .vue文件的template,通过vue-loader对其进行编译和处理
vue程序运行过程

image-20211117101613996.png

解析.vue文件

安装 npm install vue-loader@next -D (vue-loader默认是处理vue2)

同时还需要安装 npm install @vue/compiler-sfc -D (以前是@vue/vue-template-compiler)

同时还需要一个插件, 引进来。const { VueLoaderPlugin } = require('vue-loader/dist/index'),并通过new 调用

2.5 webpack中的plugin

为打包的文件添加版权信息

插件名:BannerPlugin,webpack自带

webpack.config.js+

const webpack = require('webpack')

plugins: [
    new webpack.BannerPlugin('最终版权归翟思丰所有')
]

打包的HTML的plugin

真实发布项目时,发布的是dist文件夹的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义

所以,我们需要将index.html文件打包到dist文件夹中,这个时候可以使用HtmlWebpackPlugin插件。

安装:npm i -d html-webpack-plugin (非webpack自带)

注意,使用这个插件需要删除之前在output中添加的publicPath属性,否则插入的script中的src可能会有问题

const HtmlWebpackPlugin = require('html-webpack-plugin')

plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html'
    })
]

html-webpack-plugin做了什么?

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

js压缩的plugin

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

插件:uglifyjs-webpack-plugin

安装 npm i -d uglifyjs-webpack-plugin

配置:

const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')

plugins: [
    new uglifyjsWebpackPlugin()
]

2.6 devSever

为了完成自动编译,webpack提供了几种方式:

  • webpack watch mode
  • webpack-dev-sever(常用)
  • webpack-dev-middleware

webpack watch mode

在该模式下,只要一个文件发生更新,那代码将被重新编译

如何开启watch呢?

方式一,在导出配置中,添加watch:true

方式二,在启动webpack的命令中,添加 --watch 参数

webpack-dev-sever

  1. npm install webpack-dev-server -D 命令,安装支持项目自动打包的工具
  2. 修改 package.json -> scripts 中的dev命令如下:
  3. "scripts": {
    	"serve": "webpack serve"//script节点下的脚本,可以通过npm run 执行
    }
    
  4. 运行 npm run serve 命令,重新进行打包
  5. 在浏览器中访问 http://localhost:8080(可变) 地址,查看自动打包效果

注意:

webpack-dev-server帮我们打包成功的文件,并没有以文件的形式输出,而是将bundle文件保存到内存中了,(一般的打包都是输出成文件,然后再放到内存中),这样可以提高开发效率。它使用了一个叫memfs的库

contentBase

devServer: {
    contentBase: './public'
}

当在打包后的资源里找不到相关资源时,会去contentBase指定的路径里找

hot

模块热替换(Hot Module Replacement)HMR

是指应用程序运行过程中,替换、添加、删除模块,而无需刷新整个页面

为什么需要模块热替换?

当一个写了小修改,由于webpack-dev-server会自动帮我们编译并刷新页面,导致某些状态被修改掉了(期望保留这些状态),这不是我们期望的,(期望:替换、添加、删除模块时,无需刷新整个页面),而且只更新修改的模块,性能提高

webpack-dev-server已经默认开启

当然也可以自己配置

target: 'web',
devSever: {
    hot: true
}

到这里还不没做到模块热替换

你还需要在main.js里加上这么一个逻辑

if(module.hot) {
    module.hot.accpet(需要HMR的模块, () => {})
}

回调函数可以进行其它处理,可省。

当然,要是很多模块需要HMR,那这样写起来会非常麻烦。

真实开发中,vue-loader已经帮我们做了(react是react-refresh,React Hot Loader已弃用)

HMR的原理是什么呢?

webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket)

Socket服务是长连接,什么是长链接?

在通信两端有通道之后,可以实时任意时刻通信(比如微信、直播)

那短连接是什么样的?

客户端发请求 --> 和服务器建立连接 --> 服务器响应 --> 断开连接

这样就是短连接

所以HMR的原理:

  • 长链接有一个好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
  • 当服务器监听到对应的模块发生变化时,会生成两个文件: manifest.json和update chunk.js
  • 通过长连接,服务器可以直接将这两个文件主动发送给客户端(浏览器)
  • 浏览器拿到这两个新文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新

host

设置主机地址

默认localhost

如果希望其它ip的主机也可以访问,可以设置0.0.0.0

localhost和0.0.0.0的区别

  • localhost:本质上是一个域名,通常情况会被解析成127.0.0.1;
  • 127.0.0.1是一个回环地址(Loop Back Address) ,回环地址发出去的包,直接被自己接收
  • 正常的数据包是应用层 - 传输层 - 网络层 - 数据链路层;
  • 回环地址主机发出的包,是在网络层直接就被获取到了,不走数据链路层了。
  • 这样,其它ip地址的主机就收不到回环地址主机发出的包了
  • 比如,监听127.0.01时,在同一网段下的主机中,通过ip地址是不能访问
  • 0.0.0.0监听IPV4上所有地址,在根据端口找到不同的应用程序,在同一网段下的主机中,通过ip地址是可以访问的(比如两台电脑连同一个wifi就可以访问项目啦)
devSever: {
    host: 'localhost'
}

port

设置端口

devSever: {
    host: 'localhost'
}

open

设置编译完成自动打开浏览器

devSever: {
    open: true
}

compress

设置静态资源压缩成gzip格式(你放心,浏览器可以识别),大概可压缩60%(不压缩html)。

devSever: {
    compress: true
}

proxy

使用代理处理跨域问题

proxy: {
    '^/api': {
        target: 'http://localhost:8888',
        pathRewrite: {
            '^/api': ''
        },
        changeOrigin: true
    }
}

为什么要设置pathRewrite: { '^api': '' }

因为请求中会出现多出/api,代理时将它重写为空

为什么要设置changeOrigin: true

有些服务器会对请求头进行校验,虽然proxy进行了代理处理了跨域,但请求头里的源还是代理之前的源,而设置了该属性为true时,代理的同时将请求头的源改成代理之后的源

historyApiFallback

解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误

devSever: {
    historyApiFallback: true
}

2.7 resolve

设置模块如何被解析

  • 开发中会有各种模块依赖,这些模块可能是自己编写的代码,也可能来自第三方库
  • resolve可以帮助webpack从每个require/import语句中,找到需要引入的合适模块代码
  • webpack使用enhanced-resolve来解析文件路径

webpack能解析3种文件路径:

  • 绝对路径,直接获取模块,不需要进一步解析
  • 相对路径,使用import/require的资源文件所在目录,被认为是上下文目录,在import/require中给定的相对路径,会拼接上下文路径,来生成模块的绝对路径
  • 模块路径,在resolve.modules中指定的所有目录检索模块(默认值是['node_modules']);可以通过设置别名的方式来替换初始模块路径(alias)

extensions

解析到文件时自动添加后缀名

默认值是 ['.wasm', '.mjs', '.js', '.json']

导入模块时,以上这4种后缀的文件就可以不写后缀啦,当然,你也可以添加其它后缀

resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.json']
}

alias

路径起别名

当有些模块层级比较深时,写起来比较麻烦,此时就可以使用别名

resolve: {
    alias: {
        '@': resolve('src'),
        'assets': resolve('@/assets')
        'components': resolve('@/componenets'),
        'views': resolve('@/view')
    }
}
  • import时用@
  • src="url"时用~

2.8 开发和生产环境的配置分离

1.在根目录新建一个文件夹config,并在里面新建三个文件

  • common.config.js(公共配置)
  • dev.config.js(开发时配置)
  • prod.config.js(发布时配置)

common.config.js

开发和生产都需要的配置

const path = require("path");
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    // publicPath: 'dist/'
  },
  module: {
    rules: [
      { test: /.css$/, use: [ 'style-loader', 'css-loader' ] },
      { 
        test: /.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 90 * 1024,
              name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      // exclude为排除项,表示babel-loader不需要处理node_modules中的js文件
      { 
        test: /.js$/, 
        exclude: /(node_modules|bower_components)/,
        use: {
            loader: 'babel-loader', 
            options: {
                presets: ['@babel/preset-env']
            }
        }
      },
      { 
        test: /.vue$/,
        use: ['vue-loader']
      },
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new VueLoaderPlugin(),
    new webpack.BannerPlugin('最终版权归翟思丰所有'),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
    // new uglifyjsWebpackPlugin()
  ],
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
}

dev.config.js

module.exports = {
  devServer: {
    contentBase: './dist',
    inline: true
  }
}

prod.config.js

const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  plugins: [
    new uglifyjsWebpackPlugin()
  ]
}

合并

安装 webpack-merge插件

既然分开了,那就合并起来

npm i -d webpack-merge

在prod.config.js+

const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const commonConfig = require('./common.config')

module.exports = webpackMerge(commonConfig, {
  plugins: [
    new uglifyjsWebpackPlugin()
  ]
})

在dev.config.js+

const webpackMerge = require('webpack-merge')
const commonConfig = require('./common.config')

module.exports = webpackMerge(commonConfig, {
  devServer: {
    contentBase: './dist',
    inline: true
  }
})

2.9 打包

分包

自己编写的代码逻辑会被统一打包到app_hash.js中,

而第三方依赖会被打包到chunk_hash.js

当自己编写的代码逻辑越来越多,打包之后的app_hash.js会越来越大,

app_hash.js越大,首屏加载时间就越长~

webpack提供了分包策略,可以将想要打包的内容放进别的文件,而不是app_hash.js

使用import() ,它的返回值是个promise

通过import函数导入的模块,webpack打包的时候,会对其进行分包操作

企业级的项目打包发布

主要的发布流程:

  • 生成打包报告,根据报告分析具体的优化方案
  • tree-shaking
  • 为第三方库启用cdn加载
  • 配置组件的按需加载
  • 自定义首页内容

3 Source Map

3.1 生产环境遇到的问题

前端项目在投入生产前,都需要对JavaScript源代码进行混淆压缩,从而减小文件的体积,提高文件的加载效率。此时就不可避免的产生了另一个问题:

对压缩混淆之后的代码除错(debug)是一件及其困难的事情

  • 变量被替换成没有任何语义的名称
  • 空行和注释被剔除

3.2 什么是Source Map

是一个信息文件,里面存储着位置信息。也就是说,Source Map文件存储着代码压缩混淆后的前后对应关系

有了它,除错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大方便后期的调试

3.3 webpack开发环境下的Source Map

开发环境下,webpack 默认启用了Source Map功能.当程序运行出错时,可以直接在控制台提示错误行的位置,并定位到具体的源代码

默认Source Map的问题

开发环境下默认生成source map,记录的是生成后的代码的位置。会导致运行时报错的行数源代码的行数不一致的问题

解决默认source map的问题

开发环境下,在webpack.config.js中添加如下的配置

module.exports = {
    mode: 'development',
    devtool: 'eval-source-map'
}

3.4 source map的最佳实践

开发环境:

  • 建议把devtool的值设置为eval-source-map
  • 好处:可以精准定义到具体的错误行

生产环境:

  • 建议关闭source map或将devtool的值设置为nosources-source-map
  • 好处:防止源码泄露,安全

4 vue脚手架

4.1 Vue-CLI

用于快速生成vue项目基础架构,其官网为:cli.vuejs.org/zh/

CLI 是Command-Line interface,命令行界面,俗称脚手架

使用步骤

安装vue脚手架

npm install @vue/cli -g

如果要更新

npm update @vue/cli -g

基本用法

使用Vue CLI创建vue项目

// 1.基于 交互式命令行 的方式,创建 新版vue项目
vue create my-project

// 2.基于 图形化界面的方式,创建 新版vue项目
vue ui

项目名称不能包含中文

注意:

不推荐这种方式,因为package.json 主要用来管理包的配置信息;为了方便维护,推荐将vue脚手架相关的配置,单独定义到 vue.config.js 配置文件中

module.exports = {
    configureWebpack: {
        ...
    }
}

vue-cli3和2区别

  • vue-cli3基于webpack 4打造,vue-cli2还是webpack3
  • vue-cli3的设计原则是”0配置“,移除的配置文件根目录下的build和config等目录
  • vue-cli3还提供vue ui命令,提供了可视化配置,更加人性化
  • 移除了static文件夹,新增了public文件夹,并且index.html移动到public

Vue-CLI原理

  1. 执行 npm run serve
  2. 根据 serve 脚本对应的命令(vue-cli-service),去 node_modules/bin下;
  3. 找到与该命令同名文件夹(vue-cli-service),并执行里面的代码;
  4. 不过vue-cli-service.js文件只是一个软连接,真正执行的代码不在这里;
  5. node_modules/@vue/cli-service下的package.json,指定了真正执行代码的位置;
  6. "bin": {
        "vue-cli-service": "bin/vue-cli-service.js"
    }
    
  7. node_modules/@vue/cli-service下有个bin目录,bin目录下有vue-cli-service.js
  8. 具体在vue-cli-service.js下做了什么,暂时不讨论。。。;

讲了这么多

主要是为了证实Vue-CLI是依赖于webpack的

4.2 Vite

发音 /vit/ ,不是/vait/~

官方定位:下一代前端构建和开发工具

组成

  • 一个开发服务器(基于原生ES模块提供了丰富的内置的功能,HMR速度非常快
  • 一套构建指令(它使用rollup打开代码,并且是预配置的,可以输出生产环境优化过的静态资源)

基本使用

在此之前,你是否想过这两个问题:现代浏览器不是基本支持es模块化了吗?我在开发阶段不使用构建工具,在打包阶段再使用构建工具可以吗?

第一个问题,你说的对。在script元素的属性里加上 type="module" ,浏览器就可以识别模块了(注意引入模块记得加后缀哦);

第二个问题,有这个想法很好。Vite的思想就类似于这个想法:开发阶段不使用构建工具,打包阶段再使用

但是,开发阶段不需要构建工具真的好吗?

开发阶段我们不止使用es模块化,还有可能有 .ts文件、.vue文件、.less文件等等,这些文件目前浏览器是不支持的,需要转化成浏览器识别的文件

不仅如此,如果不使用构建工具,包之间的依赖太多,就会发送过多的网络请求,比如lodash-es

而Vite,就可以解决这两个弊端

安装

要求node版本大于12

来个局部的 npm install vite -D

使用

因为局部安装,使用npx运行 npx vite

然后你会发现很快的它就帮我们搭建了个本地服务,而且上面两个弊端也解决啦

特性

  • css,less不需要loader,也不需要进行相关的配置啦(但是你还是要安装less哦)

  • 而postcss只需安装还有相关插件 npm install postcss postcss-preset-env -D,配置postcss.config.js即可

    // postcss.config.js
    module.exports = {
    	plugins: {
    		require('postcss-preset-env')
    	}
    }
    
  • 直接支持ts,不需要相关配置

  • vue,只需要安装相关插件,并配置vite.config.js,并且安装 @vue/compiler-sfc 插件

    vue3单文件组件@vitejs/plugin-vue
    vue3 JSX@vitejs/plugin-vue-jsx
    vue2underfin/vite-plugin-vue2

    vite.config.js

    const vue = require('@vitejs/plugin-vue')
    module.exports = {
    	plugins: {
    		vue()
    	}
    }
    

Vite2原理

vite2是如何做到对less、ts等等的支持的呢?

  1. 它会开启一个本地服务器,使用了一个叫Connect的库(vite1用koa),将对ts、less文件的请求拦截并转发
  2. ts、less等文件交给相关工具,转化es6的对应的js
  3. 相关工具返回那些js本地服务器
  4. 本地服务器再将那些js传给浏览器

同时,执行vite的时候,第一次会进行预打包,类似于缓存,下次再执行vite时就快很多

还有,vite也依赖ESBuild

  • 构建速度快,不需要缓存;
  • 支持ES6CommonJS的模块化;
  • 支持ES6的Tree Shaking
  • 支持Go、js的API;
  • 支持ts、jsx等语法编译;
  • 支持Source Map
  • 支持代码压缩
  • 支持扩展其它插件;

为什么ESBuild这么快呢?

  • 使用Go语言编写,直接将ast转化成机器代码,无需经过字节码;
  • 充分利用CPU多核,尽可能让他们饱和运行;
  • 源码从0开始编写,并且不使用第三方库,一开始就考虑了各种性能问题

快,不是没有理由的~

打包

npx vite build

预览

npx vite preview

当然这些命令可以自定义成脚本的形式~

vite的脚手架

当然,真实开发我们也不会从0开始搭建,而是使用vite的脚手架

安装

来个全局

npm install @vitejs/create-app -g

使用

create-app 项目名

5 Element-UI的基本使用

一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库。

官网地址为:element-cn.eleme.io/#/zh-cn

5.1 基于命令行方式手动安装

  1. 安装依赖包 npm i element-ui -s
  2. main.js导入element-ui相关资源
// 导入组件库
import ElementUI from 'element-ui';
// 导入组件相关样式
import 'element-ui/lib/theme-chalk/index.css'
// 配置Vue插件
Vue.use(ElementUI);

5.2 基于图形化的界面自动安装

  1. 运行 vue ui 命令,打开图形化界面
  2. 通过 vue 项目管理器,进入具体的项目配置面板
  3. 点击 插件->添加插件,进入插件查询面板
  4. 搜索 vue-cli-plugin-element 并安装
  5. 配置插件,实现按需导入,从而减少打包后项目的体积