作者:前端森林
转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ
由于业务需要,近期团队要搞一套自己的UI组件库,框架方面还是Vue。而业界已经有比较成熟的一些UI库了,比如ElementUI、AntDesign、Vant等。
结合框架Vue,我们选择在ElementUI基础上进行改造。但造轮子绝非易事,首先需要先去了解它整个但构建流程、目录设计等。
本文通过分析ElementUI完整的构建流程,最后给出搭建一个完备的组件库需要做的一些工作,希望对于想了解ElementUI源码或者也有搭建UI组件库需求的你,可以提供一些帮助!
我们先来看下ElementUI的源码的目录结构。
说完文件目录,剩下还有几个文件(常见的.babelrc、.eslintc这里就不展开说明了),在业务代码中是不常见的:
深入了解构建流程前,我们先来看下ElementUI 源码的几个比较主要的文件目录,这对于后面研究ElementUI的完整流程是有帮助的。
通常我们去看一个大型项目都是从package.json文件开始看起的,这里面包含了项目的版本、入口、脚本、依赖等关键信息。
我这里拿出了几个关键字段,一一的去分析、解释它的含义。
项目的入口文件
❝
import Element from 'element-ui' 时候引入的就是main中的文件
❞
lib/element-ui.common.js是commonjs规范,而lib/index.js是umd规范,这个我在后面的打包模块会详细说明。
指定npm publish发包时需要包含的文件/目录。
TypeScript入口文件。
项目的线上地址
当你把一个包发布到npm上时,它同时应该也可以在unpkg上获取到。也就是说,你的代码既可能在NodeJs环境也可能在浏览器环境执行。为此你需要用umd格式打包,lib/index.js是umd规范,由webpack.conf.js生成。
声明样式入口文件,这里是lib/theme-chalk/index.css,后面也会详细说明。
开发、测试、生产构建,打包、部署,测试用例等相关脚本。scripts算是package.json中最重要的部分了,下面我会一一对其中的重要指令进行说明。
"bootstrap": "yarn || npm i"
安装依赖, 官方推荐优先选用yarn(吐槽一句:我刚开始没看明白,想着bootstrap不是之前用过的那个 ui 库吗 ,后来看了下,原来bootstrap翻译过来是引导程序的意思,这样看看也就大概理解了 )
该指令主要用来自动化生成一些文件。
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"
这条指令较长,我们拆开来看:
解析icon.scss,把所有的icon的名字放在icon.json里面 最后挂在Vue原型上的$icon上。
最后通过遍历icon.json,得到了官网的这种效果:
根据components.json文件,生成src/index.js文件,核心就是json-templater/string插件的使用。
我们先来看下src/index.js文件,它对应的是项目的入口文件,最上面有这样一句:
/* Automatically generated by './build/bin/build-entry.js' */
也就是src/index.js文件是由build/bin/build-entry.js脚本自动构建的。我们来看下源码:
// 根据components.json生成src/index.js文件// 引入所有组件的依赖关系var Components = require('../../components.json');var fs = require('fs');// https://www.npmjs.com/package/json-templater 可以让string与变量结合 输出一些内容var render = require('json-templater/string');// https://github.com/SamVerschueren/uppercamelcase 转化为驼峰 foo-bar >> FooBarvar uppercamelcase = require('uppercamelcase');var path = require('path');// os.EOL属性是一个常量,返回当前操作系统的换行符(Windows系统是,其他系统是)var endOfLine = require('os').EOL;// 生成文件的名字和路径var OUTPUT_PATH = path.join(__dirname, '../../src/index.js');var IMPORT_TEMPLATE = 'import {
{name}} from '../packages/{
{package}}/index.js';';var INSTALL_COMPONENT_TEMPLATE = ' {
{name}}';// var MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */// ...// 获取所有组件的名字,存放在数组中var ComponentNames = Object.keys(Components);var includeComponentTemplate = [];var installTemplate = [];var listTemplate = [];ComponentNames.forEach(name => { var componentName = uppercamelcase(name); includeComponentTemplate.push(render(IMPORT_TEMPLATE, { name: componentName, package: name })); if (['Loading', 'MessageBox', 'Notification', 'Message', 'InfiniteScroll'].indexOf(componentName) === -1) { installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, { name: componentName, component: name })); } if (componentName !== 'Loading') listTemplate.push(` ${componentName}`);});var template = render(MAIN_TEMPLATE, { include: includeComponentTemplate.join(endOfLine), install: installTemplate.join(',' + endOfLine), version: process.env.VERSION || require('../../package.json').version, list: listTemplate.join(',' + endOfLine)});// 结果输出到src/index.js中fs.writeFileSync(OUTPUT_PATH, template);console.log('[build entry] DONE:', OUTPUT_PATH);
其实就是上面说的,根据components.json,生成src/index.js文件。
根据 examples/i18n/page.json 和模版,生成不同语言的 demo,也就是官网 demo 展示国际化的处理。
ElementUI官网的国际化依据的模版是examples/pages/template,根据不同的语言,分别生成不同的文件:
这里面都是.tpl文件,每个文件对应一个模板,而且每个tpl文件又都是符合SFC规范的Vue文件。
我们随便打开一个文件:
export default { data() { return { lang: this.$route.meta.lang, navsData: [ { path: '/design', name: '' }, { path: '/nav', name: '' } ] }; }};
里面都有数字标示了需要国际化处理的地方。
首页所有国际化相关的字段对应关系存储在examples/i18n/page.json中:
最终官网展示出来的就是经过上面国际化处理后的页面:
支持切换不同语言。
绕了一圈,回到主题:build/bin/i18n.js帮我们做了什么呢?
我们思考一个问题:首页的展示是如何做到根据不同语言,生成不同的vue文件呢?
这就是build/bin/i18n.js帮我们做的事情。
来看下对应的源码:
'use strict';var fs = require('fs');var path = require('path');var langConfig = require('../../examples/i18n/page.json');langConfig.forEach(lang => { try { fs.statSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`)); } catch (e) { fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`)); } Object.keys(lang.pages).forEach(page => { var templatePath = path.resolve(__dirname, `../../examples/pages/template/${ page }.tpl`); var outputPath = path.resolve(__dirname, `../../examples/pages/${ lang.lang }/${ page }.vue`); var content = fs.readFileSync(templatePath, 'utf8'); var pairs = lang.pages[page]; Object.keys(pairs).forEach(key => { content = content.replace(new RegExp(``, 'g'), pairs[key]); }); fs.writeFileSync(outputPath, content); });});
处理流程也很简单:遍历examples/i18n/page.json,根据不同的数据结构把tpl文件的标志位,通过正则匹配出来,并替换成自己预先设定好的字段。
这样官网首页的国际化就完成了。
根据package.json中的version,生成examples/versions.json,对应就是完整的版本列表
处理样式相关。
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
同样这一条也关联了多个操作,我们拆开来看。
这一步是根据components.json,生成package/theme-chalk/index.scss文件,把所有组件的样式都导入到index.scss。
其实是做了一个自动化导入操作,后面每次新增组件,就不用手动去引入新增组件的样式了。
我们都知道ElementUI在使用时有两种引入方式:
import Vue from 'vue';import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';import App from './App.vue';Vue.use(ElementUI);new Vue({ el: '#app', render: h => h(App)});
import Vue from 'vue';import { Pagination, Dropdown } from 'element-ui';import App from './App.vue';Vue.use(Pagination)Vue.use(Dropdown)new Vue({ el: '#app', render: h => h(App)});
对应两种引入方式,Element在打包时对应的也有两种方案。
具体如下:将packages/theme-chalk下的所有scss文件编译为css,当你需要全局引入时,就去引入index.scss文件;当你按需引入时,引入对应的组件scss文件即可。
这其中有一点,我们需要思考下:如何把packages/theme-chalk下的所有scss文件编译为css?
在平时的开发中,我们打包、压缩之类的工作往往都会交给webpack去处理,但是,针对上面这个问题,我们如果采用gulp基于工作流去处理会更加方便。
gulp相关的处理就在packages/theme-chalk/gulpfile.js中:
'use strict';const { series, src, dest } = require('gulp');const sass = require('gulp-sass'); // 编译gulp工具const autoprefixer = require('gulp-autoprefixer'); // 添加厂商前缀const cssmin = require('gulp-cssmin'); // 压缩cssfunction compile() { return src('./src/*.scss') // src下的所有scss文件 .pipe(sass.sync()) // 把scss文件编译成css .pipe(autoprefixer({ // 基于目标浏览器版本,添加厂商前缀 browsers: ['ie > 9', 'last 2 versions'], cascade: false })) .pipe(cssmin()) // 压缩css .pipe(dest('./lib')); // 输出到lib下}function copyfont() { return src('./src/fonts/**') // 读取src/fonts下的所有文件 .pipe(cssmin()) .pipe(dest('./lib/fonts')); // 输出到lib/fonts下}exports.build = series(compile, copyfont);
经过处理,最终就会打包出对应的样式文件
❝
cp-cli 是一个跨平台的copy工具,和CopyWebpackPlugin类似
❞
这里就是复制文件到lib/theme-chalk下。
上面提到过多次components.json,下面就来了解下。
这个文件其实就是记录了组件的路径,在自动化生成文件以及入口时会用到:
{ "pagination": "./packages/pagination/index.js", "dialog": "./packages/dialog/index.js", "autocomplete": "./packages/autocomplete/index.js", // ... "avatar": "./packages/avatar/index.js", "drawer": "./packages/drawer/index.js", "popconfirm": "./packages/popconfirm/index.js"}
存放着组件库的源码和组件样式文件。
这里以Alert组件为例做下说明:
这里main.vue对应就是组件源码,而index.js就是入口文件:
import Alert from './src/main';/* istanbul ignore next */Alert.install = function(Vue) { Vue.component(Alert.name, Alert);};export default Alert;
引入组件,然后为组件提供install方法,让Vue可以通过Vue.use(Alert)去使用。
❝
关于install可以看官方文档
❞
这里面存放的就是所有组件相关的样式,上面也已经做过说明了,里面有index.scss(用于全局引入时导出所有组件样式)和其他每个组件对应的scss文件(用于按需引入时导出对应的组件样式)
说了半天,终于找到了src文件夹。
上面的packages文件夹是分开去处理每个组件,而src的作用就是把所有的组件做一个统一处理,同时包含自定义指令、项目整体入口、组件国际化、组件 mixins、动画的封装和公共方法。
我们主要来看下入口文件,也就是src/index.js:
/* Automatically generated by './build/bin/build-entry.js' */// 导入了packages下的所有组件import Pagination from '../packages/pagination/index.js';import Dialog from '../packages/dialog/index.js';import Autocomplete from '../packages/autocomplete/index.js';// ...const components = [ Pagination, Dialog, Autocomplete, // ...];// 提供了install方法,帮我们挂载了一些组件与变量const install = function(Vue, opts = {}) { locale.use(opts.locale); locale.i18n(opts.i18n); // 把所有的组件注册到Vue上面 components.forEach(component => { Vue.component(component.name, component); }); Vue.use(InfiniteScroll); Vue.use(Loading.directive); Vue.prototype.$ELEMENT = { size: opts.size || '', zIndex: opts.zIndex || 2000 }; Vue.prototype.$loading = Loading.service; Vue.prototype.$msgbox = MessageBox; Vue.prototype.$alert = MessageBox.alert; Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$prompt = MessageBox.prompt; Vue.prototype.$notify = Notification; Vue.prototype.$message = Message;};/* istanbul ignore if */if (typeof window !== 'undefined' && window.Vue) { install(window.Vue);}// 导出版本号、install方法(插件)、以及一些功能比如国际化功能export default { version: '2.13.2', locale: locale.use, i18n: locale.i18n, install, Pagination, Dialog, Autocomplete, // ...};
文件开头的:
/* Automatically generated by './build/bin/build-entry.js' */
其实在上面的scripts的build/bin/build-entry.js中我们已经提到过:src/index.js是由build-entry脚本自动生成的。
这个文件主要做下以下事情:
存放了 ElementUI的组件示例。
其实从目录结构,我们不难看出这是一个完整独立的Vue项目。主要用于官方文档的展示:
这里我们主要关注下docs文件夹:
Element官网支持 4 种语言,docs一共有 4 个文件夹,每个文件夹里面的内容基本是一样的。
我们可以看到里面全部都是md文档,而每一个md文档,分别对应着官网组件的展示页面。
❝
其实现在各大主流组件库文档都是用采用md编写。
❞
我们上面大致了解了源码的几个主要文件目录,但是都比较分散。下面我们从构建指令到新建组件、打包流程、发布组件完整的看一下构建流程。
平时我们都习惯将项目常用的脚本放在package.json中的scripts中。但ElementUI还使用了Makefile文件(由于文件内容较多,这里就选取了几个做下说明):
.PHONY: dist testdefault: help# build all themebuild-theme:npm run build:themeinstall:npm installinstall-cn:npm install --registry=http://registry.npm.taobao.orgdev:npm run devplay:npm run dev:playnew:node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))dist: installnpm run distdeploy:@npm run deploypub:npm run pubtest:npm run test:watch// Tip:// make new [中文]// 1、将新建组件添加到components.json// 2、添加到index.scss// 3、添加到element-ui.d.ts// 4、创建package// 5、添加到nav.config.json
我是第一次见,所以就去Google下,网上对Makefile对定义大概是这样:
❝
Makefile 是一个适用于 C/C++ 的工具,较早作为工程化工具出现在 UNIX 系统中, 通过 make 命令来执行一系列的编译和连接操作。在拥有 make 环境的目录下, 如果存在一个 Makefile 文件。那么输入 make命令将会执行 Makefile 文件中的某个目标命令。
❞
这里我以make install为例简要说明下执行流程:
我们看下scripts中的dev指令:
"dev":"npm run bootstrap &&npm run build:file &&cross-env NODE_ENV=developmentwebpack-dev-server --config build/webpack.demo.js &node build/bin/template.js",
首先npm run bootstrap是用来安装依赖的。
npm run build:file在前面也有提到,主要用来自动化生成一些文件。主要是node build/bin/build-entry.js,用于生成Element的入口js:先是读取根目录的components.json,这个json文件维护着Element所有的组件路径映射关系,键为组件名,值为组件源码的入口文件;然后遍历键值,将所有组件进行import,对外暴露install方法,把所有import的组件通过Vue.component(name, component)方式注册为全局组件,并且把一些弹窗类的组件挂载到Vue的原型链上(这个在上面介绍scripts相关脚本时有详细说明)。
在生成了入口文件的src/index.js之后就会运行webpack-dev-server。
webpack-dev-server --config build/webpack.demo.js
这个前面也提过,用于跑Element官网的基础配置。
上面我们提到了,Element中还用了makefile为我们编写了一些额外的脚本。
这里重点说一下 make new [中文] 这个命令。
当运行这个命令的时候,其实运行的是 node build/bin/new.js。
build/bin/new.js比较简单,备注也很清晰,它帮我们做了下面几件事:
1、新建的组件添加到components.json
2、在packages/theme-chalk/src下新建对应到组件scss文件,并添加到packages/theme-chalk/src/index.scss中
3、添加到 element-ui.d.ts,也就是对应的类型声明文件
4、创建package(我们上面有提到组件相关的源码都在package目录下存放)
5、添加到nav.config.json(也就是官网组件左侧的菜单)
ElementUI打包执行的脚本是:
"dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
下面我们一一来进行分析:
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
删除之前打包生成文件。
根据components.json生成入口文件src/index.js,以及i18n相关文件。这个在上面已经做过分析,这里就不再展开进行说明。
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
项目eslint检测,这也是现在项目必备的。
webpack --config build/webpack.conf.js &&webpack --config build/webpack.common.js &&webpack --config build/webpack.component.js
生成umd格式的js文件(index.js)
生成commonjs格式的js文件(element-ui.common.js),require时默认加载的是这个文件。
以components.json为入口,将每一个组件打包生成一个文件,用于按需加载。
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
把src目录下的除了index.js入口文件外的其他文件通过babel转译,然后移动到lib文件夹下。
"build:umd": "node build/bin/build-locale.js",
生成umd模块的语言包。
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
根据components.json,生成package/theme-chalk/index.scss。用gulp构建工具,编译scss、压缩、输出css到lib目录。
最后用一张图来描述上述整个打包流程:
打包完成,紧跟着就是代码的发布了。Element中发布主要是用shell脚本实现的。
Element发布一共涉及三个部分:
1、git 发布
2、npm 发布
3、官网发布
发布对应的脚本是:
"pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js && sh build/deploy-faas.sh",
运行 git-release.sh 进行git冲突的检测,这里主要是检测dev分支是否冲突,因为Element是在dev分支进行开发的。
#!/usr/bin/env sh# 切换至dev分支git checkout dev# 检测本地和暂存区是否还有未提交的文件if test -n "$(git status --porcelain)"; then echo 'Unclean working tree. Commit or stash changes first.' >&2; exit 128;fi# 检测本地分支是否有误if ! git fetch --quiet 2>/dev/null; then echo 'There was a problem fetching your branch. Run `git fetch` to see more...' >&2; exit 128;fi# 检测本地分支是否落后远程分支if test "0" != "$(git rev-list --count --left-only @'{u}'...HEAD)"; then echo 'Remote history differ. Please pull changes.' >&2; exit 128;fi# 通过以上检查,表示代码无冲突echo 'No conflicts.' >&2;
dev分支代码检测没有冲突,接下来就会执行release.sh脚本,合并dev分支到master、更新版本号、推送代码到远程仓库并发布到npm(npm publish)。
官网更新大致就是:将静态资源生成到examples/element-ui目录下,然后放到gh-pages分支,这样就能通过github pages的方式访问。
到这里ElementUI的完整构建流程就分析完了。
通过对ElementUI源码文件和构建流程的分析,下面我们可以总结一下搭建一个完备的 ui 组件库都需要做什么工作。
目录结构对于大型项目是尤其重要的,合理清晰的结构对于后期的开发和扩展都是很有意义的。ui组件库的目录结构,我感觉ElementUI的就很不错:
|-- Element |-- .babelrc // babel相关配置 |-- .eslintignore |-- .eslintrc // eslint相关配置 |-- .gitattributes |-- .gitignore |-- .travis.yml // ci配置 |-- CHANGELOG.en-US.md |-- CHANGELOG.es.md |-- CHANGELOG.fr-FR.md |-- CHANGELOG.zh-CN.md // 版本改动说明 |-- FAQ.md // 常见问题QA |-- LICENSE // 版权协议相关 |-- Makefile // 脚本集合(工程化编译) |-- README.md // 项目说明文档 |-- components.json // 组件配置文件 |-- element_logo.svg |-- package.json |-- yarn.lock |-- .github // 贡献者、issue、PR模版 | |-- CONTRIBUTING.en-US.md | |-- CONTRIBUTING.es.md | |-- CONTRIBUTING.fr-FR.md | |-- CONTRIBUTING.zh-CN.md | |-- ISSUE_TEMPLATE.md | |-- PULL_REQUEST_TEMPLATE.md | |-- stale.yml |-- build // 打包 |-- examples // 示例代码 |-- packages // 组件源码 |-- src // 入口文件以及各种辅助文件 |-- test // 单元测试文件 |-- types // 类型声明
参考大多数 UI 组件库的做法,可以将 examples 下的示例代码组织起来并暴露一个入口,使用 webpack 配置一个 dev-server,后续对组件的调试、运行都在此 dev-server 下进行。
UI 组件作为高度抽象的基础公共组件,编写单元测试是很有必要的。合格的单元测试也是一个成熟的开源项目必备的。
对于打包后的文件,统一放在 lib 目录下,同时记得要在 .gitignore 中加上 lib 目录,避免将打包结果提交到代码库中。
同时针对引入方式的不同,要提供全局引入(UMD)和按需加载两种形式的包。
组件库的文档一般都是对外可访问的,因此需要部署到服务器上,同时也需具备本地预览的功能。
组件库的某个版本完成开发工作后,需要将包发布到 npm 上。发布流程:
发布后需要日常维护之前老版本,一般需要注意一下几点:
《让Jenkins自动部署你的Vue项目「实践」》
《20个免费的设计资源 UI套件背景图标CSS框架》
《Deno将停止使用TypeScript,并公布五项具体理由》
《前端骨架屏都是如何生成的》
《Vue原来可以这样写开发效率杠杠的》
《用vue简单写一个音乐播放组件「附源码」》
《为什么Vue3.0不再使用defineProperty实现数据监听?》
《「干货」学会这些Vue小技巧,可以早点下班和女神约会》
《探索 Vue-Multiselect》
《细品30张脑图带你从零开始学Vue》
《Vue后台项目中遇到的技术难点以及解决方案》
《手把手教你Electron + Vue实战教程(五)》
《手把手教你Electron + Vue实战教程(四)》
《手把手教你Electron + Vue实战教程(三)》
《手把手教你Electron + Vue实战教程(二)》
《手把手教你Electron + Vue实战教程(一)》
《收集22种开源Vue模板和主题框架「干货」》
《如何写出优秀后台管理系统?11个经典模版拿去不谢「干货」》
《手把手教你实现一个Vue自定义指令懒加载》
《基于 Vue 和高德地图实现地图组件「实践」》
《一个由 Vue 作者尤雨溪开发的 web 开发工具—vite》
《是什么让我爱上了Vue.js》
《1.1万字深入细品Vue3.0源码响应式系统笔记「上」》
《1.1万字深入细品Vue3.0源码响应式系统笔记「下」》
《「实践」Vue 数据更新7 种情况汇总及延伸解决总结》
《尤大大细说Vue3 的诞生之路「译」》
《提高10倍打包速度工具Snowpack 2.0正式发布,再也不需要打包器》
《大厂Code Review总结Vue开发规范经验「值得学习」》
《Vue3 插件开发详解尝鲜版「值得收藏」》
《带你五步学会Vue SSR》
《记一次Vue3.0技术干货分享会》
《Vue 3.x 如何有惊无险地快速入门「进阶篇」》
《「干货」微信支付前后端流程整理(Vue+Node)》
《带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」》
《「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分》
《「干货」Vue+Element前端导入导出Excel》
《「实践」Deno bytes 模块全解析》
《细品pdf.js实践解决含水印、电子签章问题「Vue篇」》
《基于vue + element的后台管理系统解决方案》
《Vue仿蘑菇街商城项目(vue+koa+mongodb)》
《基于 electron-vue 开发的音乐播放器「实践」》
《「实践」Vue项目中标配编辑器插件Vue-Quill-Editor》
《基于 Vue 技术栈的微前端方案实践》
《消息队列助你成为高薪 Node.js 工程师》
《Node.js 中的 stream 模块详解》
《「干货」Deno TCP Echo Server 是怎么运行的?》
《「干货」了不起的 Deno 实战教程》
《「干货」通俗易懂的Deno 入门教程》
《Deno 正式发布,彻底弄明白和 node 的区别》
《「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台》
《「实践」深入对比 Vue 3.0 Composition API 和 React Hooks》
《前端网红框架的插件机制全梳理(axios、koa、redux、vuex)》
《深入Vue 必学高阶组件 HOC「进阶篇」》
《深入学习Vue的data、computed、watch来实现最精简响应式系统》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(一)》
《10个实例小练习,快速入门熟练 Vue3 核心新特性(二)》
《教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」》
《2020前端就业Vue框架篇「实践」》
《详解Vue3中 router 带来了哪些变化?》
《Vue项目部署及性能优化指导篇「实践」》
《Vue高性能渲染大数据Tree组件「实践」》
《尤大大细品VuePress搭建技术网站与个人博客「实践」》
《10个Vue开发技巧「实践」》
《是什么导致尤大大选择放弃Webpack?【vite 原理解析】》
《带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】》
《带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】》
《实践Vue 3.0做JSX(TSX)风格的组件开发》
《一篇文章教你并列比较React.js和Vue.js的语法【实践】》
《手拉手带你开启Vue3世界的鬼斧神工【实践】》
《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》
《怎样为你的 Vue.js 单页应用提速》
《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》
《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》
《Vue真是太好了 壹万多字的Vue知识点 超详细!》
《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》
《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》
《手把手教你深入浅出vue-cli3升级vue-cli4的方法》
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》
《Vue3 尝鲜》
《总结Vue组件的通信》
《Vue 开源项目 TOP45》
《2020 年,Vue 受欢迎程度是否会超过 React?》
《尤雨溪:Vue 3.0的设计原则》
《使用vue实现HTML页面生成图片》
《实现全栈收银系统(Node+Vue)(上)》
《实现全栈收银系统(Node+Vue)(下)》
《vue引入原生高德地图》
《Vue合理配置WebSocket并实现群聊》
《多年vue项目实战经验汇总》
《vue之将echart封装为组件》
《基于 Vue 的两层吸顶踩坑总结》
《Vue插件总结【前端开发必备】》
《Vue 开发必须知道的 36 个技巧【近1W字】》
《构建大型 Vue.js 项目的10条建议》
《深入理解vue中的slot与slot-scope》
《手把手教你Vue解析pdf(base64)转图片【实践】》
《使用vue+node搭建前端异常监控系统》
《推荐 8 个漂亮的 vue.js 进度条组件》
《基于Vue实现拖拽升级(九宫格拖拽)》
《手摸手,带你用vue撸后台 系列二(登录权限篇)》
《手摸手,带你用vue撸后台 系列三(实战篇)》
《前端框架用vue还是react?清晰对比两者差异》
《Vue组件间通信几种方式,你用哪种?【实践】》
《浅析 React / Vue 跨端渲染原理与实现》
《10个Vue开发技巧助力成为更好的工程师》
《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》
《1W字长文+多图,带你了解vue的双向数据绑定源码实现》
《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》
《手把手教你D3.js 实现数据可视化极速上手到Vue应用》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》
《Vue3.0权限管理实现流程【实践】》
《后台管理系统,前端Vue根据角色动态设置菜单栏和路由》
作者:前端森林
转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ