part3-m4

  • 本阶段围绕当下国内最主流的前端核心框架 Vue.js 展开,深入框架内部,通过解读源码或者手写实现的方式,剖析 Vue.js 框架的内部实现原理,让你做到知其所以然。同时我们还会介绍 Vue.js 的进阶用法、周边生态以及性能优化,让你轻松应对更加复杂的项目业务需求。

模块四 搭建自己的SSR、静态站点生成(SSG)及封装 Vue.js 组件库

  • 本模块会带你深入 SSR 的实现,自己动手搭建一个自己的基于 Vue.js 的 SSR,然后使用静态站点生成器快速生成静态网站。最后会教会你如何开发自己的基础/业务组件,一起来 Happy 的造轮子,不做只会用别人组件的码农。
任务一:搭建自己的SSR
  1. 渲染一个Vue实例
  • mkdri vue-ssr
  • cd vue-ssr
  • npm init -y
  • npm i vue vue-server-renderer
  • server.js
  1. 结合到Web服务中
  • npm i express cross-env
  1. 使用HTML模板
  • index.template.html
  1. 在模板中使用外部数据
  • {{{}}} 模板中三个大括号,原文输出(html)
  1. 构建配置-基本思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFEANm12-1627836997520)(./img/1627176435770.jpg)]

  • https://ssr.vuejs.org/zh/guide/structure.html#%E4%BD%BF%E7%94%A8-webpack-%E7%9A%84%E6%BA%90%E7%A0%81%E7%BB%93%E6%9E%84
  1. 构建配置-源码结构
src
├── components
│ ├── Foo.vue
│ ├── Bar.vue
│ └── Baz.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器
<!-- App.vue -->
<template>
  <!-- 客户端渲染的入口节点 -->
  <div id="app">
  <h1>拉勾教育</h1>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>
<style>
</style>
// app.js
import Vue from 'vue'
import App from './App.vue'
// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
  const app = new Vue({
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app }
}
  • 客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中:
import { createApp } from './app'
// 客户端特定引导逻辑……
const { app } = createApp()
// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')
  • entry-server.js
    • 服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,它不会做太多事情 - 但是稍后我们将在此执行服务器端路由匹配 (server-sideroute matching) 和数据预取逻辑 (data pre-fetching logic)。
import { createApp } from './app'
export default context => {
  const { app } = createApp()
  return app
}
  • server.js
/**
* 通用应用 Web 服务启动脚本
*/
const express = require('express')
const Vue = require('vue')
const VueServerRenderer = require('vue-server-renderer')
const fs = require('fs')
// 创建一个 express 实例
const server = express()
// 生成一个渲染器
const renderer = VueServerRenderer.createRenderer({
  // 渲染器就会自动把渲染的结果注入到模板中
  template: fs.readFileSync('./index.html', 'utf-8')
})
const createApp = () => {
  const app = new Vue({
    template: `
    <div id="app">
    <h1>Hello {{ message }}</h1>
    <input v-model="message">
    </div>
    `,
    data: {
      message: 'World'
    }
  })
  return app
}
server.get('/foo', (req, res) => {
  const app = createApp()
  app.message = '世界'
  res.end('foo')
})
// 设置一个路由
server.get('/', async (req, res) => {
  // const app = new Vue({
  // template: `
  // <div id="app">
  // <h1>Hello {{ message }}</h1>
  // <input v-model="message">
  // </div>
  // `,
  // data: {
  // message: 'World'
  // }
  // })
  try {
    const app = createApp()
    const ret = await renderer.renderToString(app, {
    title: '自定义页面标题',
    meta: `
    <meta name="description" content="hello world">
    `
    })
    res.end(ret)
  } catch (err) {
    res.status(500).end('Internal Server Error.')
  }
})
// 监听端口,启动 Web 服务
server.listen(3000, () => {
  console.log('running at port 3000.')
})
  • index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拉勾教育</title>
</head>
<body>
<!-- 服务端渲染的内容出口 -->
<!--vue-ssr-outlet-->
</body>
</html>
  1. 构建配置-安装依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUfg8dhh-1627836997522)(./img/1627177187776.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZRaiNoee-1627836997524)(./img/1627177140623.jpg)]

npm i -D webpack webpack-cli webpack-merge webpack-node-externals @babel/core @babel/plugin-transform-runtime @babel/preset-env babel-loader css-loader url-loader   file-loader rimraf vue-loader vue-template-compiler friendly-errors-webpack-plugin
  1. 构建配置-webpack配置文件
  • 初始化 webpack 打包配置文件
build
├── webpack.base.config.js # 公共配置
├── webpack.client.config.js # 客户端打包配置文件
└── webpack.server.config.js # 服务端打包配置文件
  • webpack.base.config.js
/**
* 公共配置
*/
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const path = require('path')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const resolve = file => path.resolve(__dirname, file)
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
    mode: isProd ? 'production' : 'development',
    output: {
        path: resolve('../dist/'),
        publicPath: '/dist/',
        filename: '[name].[chunkhash].js'
    },
    resolve: {
        alias: {
            // 路径别名,@ 指向 src
            '@': resolve('../src/')
        },
        // 可以省略的扩展名
        // 当省略扩展名的时候,按照从前往后的顺序依次解析
        extensions: ['.js', '.vue', '.json']
    },
    devtool: isProd ? 'source-map' : 'cheap-module-eval-source-map',
    module: {
        rules: [
            // 处理图片资源
            {
                test: /\.(png|jpg|gif)$/i,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192,
                        },
                    },
                ],
            },
            // 处理字体资源
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    'file-loader',
                ],
            },
            // 处理 .vue 资源
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // 处理 CSS 资源
            // 它会应用到普通的 `.css` 文件
            // 以及 `.vue` 文件中的 `<style>` 块
            {
                test: /\.css$/,
                use: [
                    'vue-style-loader',
                    'css-loader'
                ]
            },
            // CSS 预处理器,参考:https://vue-loader.vuejs.org/zh/guide/preprocessors.html
            // 例如处理 Less 资源
            // {
            // test: /\.less$/,
            // use: [
            // 'vue-style-loader',
            // 'css-loader',
            // 'less-loader'
            // ]
            // },
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new FriendlyErrorsWebpackPlugin()
    ]
}
  • webpack.client.config.js
/**
* 客户端打包配置
*/
const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.config.js')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
module.exports = merge(baseConfig, {
    entry: {
        app: './src/entry-client.js' // 相对的是打包时候运行的路径
    },
    module: {
        rules: [
            // ES6 转 ES5
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        cacheDirectory: true,
                        plugins: ['@babel/plugin-transform-runtime']
                    }
                }
            },
        ]
    },
    // 重要信息:这将 webpack 运行时分离到一个引导 chunk 中,
    // 以便可以在之后正确注入异步 chunk。
    optimization: {
        splitChunks: {
            name: "manifest",
            minChunks: Infinity
        }
    },
    plugins: [
        // 此插件在输出目录中生成 `vue-ssr-client-manifest.json`。
        new VueSSRClientPlugin()
    ]
})
  • webpack.server.config.js
/**
* 服务端打包配置
*/
const { merge } = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.config.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
module.exports = merge(baseConfig, {
    // 将 entry 指向应用程序的 server entry 文件
    entry: './src/entry-server.js',
    // 这允许 webpack 以 Node 适用方式处理模块加载
    // 并且还会在编译 Vue 组件时,
    // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。
    target: 'node',
    output: {
        filename: 'server-bundle.js',
        // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
        libraryTarget: 'commonjs2'
    },
    // 不打包 node_modules 第三方包,而是保留 require 方式直接加载
    externals: [nodeExternals({
        // 白名单中的资源依然正常打包
        allowlist: [/\.css$/]
    })],
    plugins: [
        // 这是将服务器的整个输出构建为单个 JSON 文件的插件。
        // 默认文件名为 `vue-ssr-server-bundle.json`
        new VueSSRServerPlugin()
    ]
})
  1. 构建配置-配置构建命令
"scripts": {
  "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js",
  "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js",
  "build": "rimraf dist && npm run build:client && npm run build:server"
},
  1. 构建配置-启动应用

  2. 构建配置-解析渲染流程

  • 服务端渲染
    • renderer 是如何拿到 entry-server 模块的?
      • createBundleRenderer 中的 serverBundle
    • server Bundle 是 Vue SSR 构建的一个特殊的 JSON 文件
      • entry:入口
      • files:所有构建结果资源列表
      • maps:源代码 source map 信息
    • server-bundle.js 就是通过 server.entry.js 构建出来的结果文件
    • 最终把渲染结果注入到模板中
  • 客户端渲染
    • vue-ssr-client-manifest.json
      • publicPath:访问静态资源的根相对路径,与 webpack 配置中的 publicPath 一致
      • all:打包后的所有静态资源文件路径
      • initial:页面初始化时需要加载的文件,会在页面加载时配置到 preload 中
      • async:页面跳转时需要加载的文件,会在页面加载时配置到 prefetch 中
      • modules:项目的各个模块包含的文件的序号,对应 all 中文件的顺序;moduleIdentifier和和all数组中文件的映射关系(modules对象是我们查找文件引用的重要数据)
  1. 构建配置开发模式-基本思路
  • 生产模式
    • npm run build 构建
    • node server.js 启动应用
  • 开发模式
    • 监视代码变动自动构建,热更新等功能
    • node server.js 启动应用
"scripts": {
  // ...
  // 启动开发服务
  "dev": "node server.js",
  // 启动生产服务
  "start": "cross-env NODE_ENV=production && node server.js"
}
  1. 构建配置开发模式-提取处理模块

  2. 构建配置开发模式-update更新函数

  3. 构建配置开发模式-处理模板文件

  4. 构建配置开发模式-服务端监视打包

  5. 构建配置开发模式-把数据写入内存中

  • npm install webpack-dev-middleware --save-dev 把打包的结果放入内存
  1. 构建配置开发模式-客户端构建

  2. 构建配置开发模式-热更新

  3. 编写通用应用注意事项

  4. 路由处理-配置VueRouter

  5. 路由处理-将路由注册到根实例

  6. 路由处理-适配服务端入口

  7. 路由处理-服务端server适配

  8. 路由处理-适配客户端入口

  9. 路由处理-处理完成

  10. 管理页面Head内容

  • vue-meta
  1. 数据预取和状态管理-思路分析

  2. 数据预取和状态管理-数据预取

  • npm i vuex
  1. 数据预取和状态管理-将预取数据同步到客户端
任务二:静态站点生成
  1. 关于本任务中gridsome安装包下载不下来解决方法

  2. Gridsome基础-介绍

  • 一个免费、开源、基于Vue.js技术栈的静态网站生成器
  • 官方网站:https://gridsome.org/
  • 什么是静态网站生成器
    • 静态网站生成器是使用一系列配置、模版以及数据,生成静态HTML文件及相关资源的工具
    • 这个功能也叫预渲染
    • 生成的网站不需要类似PHP这样的服务器
    • 只想要放到支持静态资源的 Web Server 或 DCN 上即可运行
  • 静态网站的好处
    • 省钱:不需要专业的服务器,只要能托管静态文件的空间即可
    • 快速:不经过后端服务器的处理,只传输内容
    • 安全:没有后端程序的执行,自然会更安全
  • 常见的静态网站生成器
    • Jekyll(Ruby)
    • Hexo(Node)
    • Hugo(Golang)
    • Gatsby(Node/React)
    • Gridsome(Node/Vue)
    • 另外,Next.js,Nuxt.js 也能生成静态网站,但是它们更多被认为是SSR(服务端渲染)框架。
  • JAMStack
    • 这类静态网站生成器还有个漂亮的名字叫JAMStack
    • JAMStack的JAM是JavaScript、API和Markup的首字母组合
    • 本质上是一种胖前端,通过调用各种API来实现更多的功能
    • 其实也是一种前后端的模式,只不过离得比较开,甚至前后端来自不同的厂商
  • 静态应用的使用场景
    • 不适合有大量路由页面的应用
      • 网站页面多,预渲染将非常缓慢
    • 不合适有大量动态内容的应用
  1. Gridsome基础-创建Gridsome项目
  • Gridsome 依赖 sharp (处理图片,图片的大小,格式转换)
    • 1.https://sharp.pixelplumbing.com/install#chinese-mirror 修改镜像方便安装
    • 2.node-gyp Gridsome依赖c++环境编译 https://github.com/nodejs/node-gyp
    • 3.强行打断安装,然后删除安装来一半的mode-modules ,npm i 重新安装
  1. Gridsome基础-预渲染
  • npm i -g serve 基于node开发的静态web服务
  • serve dist 跟上项目的静态文件目录
  • 客户端交互都是单页面应用,除非强制刷新页面
  1. Gridsome基础-目录结构

    http; 目录结构

  2. Gridsome基础-Pages

  3. Gridsome基础-添加集合

  • http; 提供数据的网站
  1. Gridsome基础-添加集合
  • Importing data
// gridsome.server.js
const axios = require('axios')
module.exports = function (api) {
  api.loadSource(async actions => {
    const { posts } = await axios.get('https://api.example.com/posts')

    const collection = actions.addCollection({
      typeName: 'BlogPosts'
    })

    for (const post of posts) {
      collection.addNode({
        id: post.id,
        title: post.title
      })
    }
  })
}
  1. Gridsome基础-在GraphQL中查询数据
  • 查看数据 http://localhost:8080/___explore
query {
  blogPosts(id: 1) {
    id,
    title
  }
}
  1. Gridsome基础-在页面中查询GraphQL
  • Collections > query nodes in GraphQL > Querying data
  • serve dist/ 启动静态文件服务器查看
  1. Gridsome基础-使用模板渲染节点页面
  • Templates
  • 可以批量生成列表也对应的详情页
  1. Gridsome案例-创建项目
  • gridsome create blog-with-gridsome
  1. Gridsome案例-处理首页模板
  • http; 模版地址
  • fork 到自己的仓库,防止开源库作者删除项目
  • git clone 地址 --depth=1 只下载最后一次提交的历史
  • cd 到一个项目中 code -a . 两个同一目录的项目打开到同一个编辑器中
  • blog-with-gridsome
    • main.js 加载css
    • npm i bootstrap @fortawesome/fontawesome-free
    • import ‘bootstrap/dist/css/bootstrap.min.css’ import ‘@fortawesome/fontawesome-free/css/all.min.css’
    • src>assets/css/index.css @import url(“字体图标地址”)
  1. Gridsome案例-处理首页模板
  • layout留一个插槽
  1. Gridsome案例-使用本地md文件管理文章内容

  2. Gridsome案例-Strapi介绍

  • http; strapi官网
  • 可以搭建一个后台管理系统
  1. Gridsome案例-Strapi基本使用

  2. Gridsome案例-使用Strapi接口数据

  3. Gridsome案例-访问受保护的API

  4. Gridsome案例-通过GraphQL访问Strapi

  • yarn strapi install graphql
  • Strapi plugins > GraphQL 可以在这里通过GraphQL访问Strapi
  1. Gridsome案例-将Strapi数据预取到Gridsome应用中
  • 在 gridsome 官网中plugins中搜索 strapi
  • 通过插件加载 strapi 中的数据
  1. Gridsome案例-设计文章和标签数据模型

  2. Gridsome案例-展示文章列表

  3. Gridsome案例-文章列表分页

  • Paginate data
  1. Gridsome案例-展示文章详情

  2. Gridsome案例-处理Markdown格式的文章内容

  • markdown-it
  1. Gridsome案例-文章标签

  2. Gridsome案例-基本设置

  3. Gridsome案例-联系我

  4. Gridsome案例-部署Strapi

  • strapi > configurations > Database 设置数据库 使用mysql 数据库
  • 把代码传到远程仓库,登陆远程服务器拉取代码
  • pm2 start npm – run start --name blog-backend
  • 远程地址:1337/admin
  1. Gridsome案例-把本地服务联通远程Strapi
  • Environment variables 配置环境变量
  • 项目中访问环境变量可以混入
Vue.mixin({
  data() {
    return {
      GRIDSOME_API_URL: process.env.GRIDSOME_API_URL
    }
  }
})
  1. Gridsome案例-部署Gridsome应用
  • http;vercel
    • import project 可以帮助托管部署静态应用项目
任务三:封装 Vue.js 组件库
  1. 课程目标
  • CDD(Component-Driven Development)
    • 自下而上
    • 从组件级别开始,到页面级别结束
  • CDD 的好处
    • 组件在最大程度被重用
    • 可视化测试
  • 课程介绍
    • 处理组件的边界情况
    • 快速原型开发
    • 组件开发
    • Storybook 用于隔离开发组件,方便管理组件库和对组件进行测试
    • Monorepo 项目结构的一种组织方式,有利于管理组件库的结构
    • 基于模版生成包的结构
    • Lerna + yarn workspaces 为了方便管理包的依赖,以及方便发布和提交组件
    • 组件测试
    • Rollup 打包
  1. 处理组件的边界情况
  • $root 根示例,响应式
  • $parent 响应式
  • $children 响应式 是一个数组
  • ref=“txt” $refs.txt 响应式
  • 父组件提供成员provide 子组件注入成员inject,不是响应式的
  1. attrs-listeners
  • $attrs
  • 把父组件中非prop属性绑定到内部组件
  • $listeners
  • 把父组件中的DOM对象的原生事件绑定到内部组件
  1. 从父组件传给自定义自组件的属性,如果没有 prop 接收,会自动设置到子组件内部的最外层标签上,如果是class 和style 的话,会合并到最外层标签的 calss 和style

  2. 如果子组件中不想继承父组件传入的非prop属性,可以在export default中使用 inheritAttrs:false 禁用继承。然后通过 v-bind="$attrs" 把尾部传入的非prop属性设置给希望的标签上。但是这不会改变class 和 style

  3. 注册事件 < input @input=“ e m i t ( ′ i n p u t ′ , emit('input', emit(input,event)” @focus=“ e m i t ( ′ f o c u s ′ , emit('focus', emit(focus,event)”>

  4. v-on="$listeners" 展开父组件传递给自组件的多个事件

  5. 快速原型开发

  • VueCLI 中提供了一个插件可以进行原型快速开发
  • 需要先额外安装一个全局的扩展
    • npm install -g @vue/cli-service-global
  • 使用vue serve 快速查看组件的运行效果
  • vue serve
    • vue serve 如果不指定参数默认会在当前目录找以下的入口文件
    • main.js、index.js、App.vue、app.vue
  • 可以指定要加载的组件
    • vue serve ./src/login.vue
  1. 快速原型开发-ElementUI
  • 安装ElementUI
    • 初始化 package.json npm init -y
    • 安装 ElementUI vue add element (需要先有vue-cli)
    • 加载 ElementUI, 使用Vue.use()安装插件
  • main.js
import Vue from 'vue'
import ElementUI from 'element-ui'

import 'element-ui/lib/theme-chalk/index.css'
import Login from './src/Login.vue'

Vue.use(ElementUI)

new Vue({
    el: '#app',
    render: h => h(Login)
})
  1. 组件开发-步骤条组件
  • 第三方组件 基础组件 业务组件
  1. 组件开发-表单组件-上

  2. 组件开发-表单组件-下

  3. 组件开发-表单组件-表单验证-上

  4. 组件开发-表单组件-表单验证-下

  • input 组件验证
  • input 组件中触发自定义事件 validate
  • Formitem 渲染完毕注册自定义事件 validate
  1. Monorepo
  • 两种项目的组织方式
  • Multirepo(Multiple Repository)
    • 每一个包对应一个项目
  • Monorepo(Monolithic Repository)
    • 一个项目仓库中管理多个模块/包(package 文件夹下放所有包)
  1. Storybook上 (故事书)
  • 可视化的组件展示平台
  • 在隔离的开发环境中,以交互式的方式展示组件
  • 独立开发组件
  • 支持框架
    • React、React Native、Vue、Angular
    • Ember、HTML、Svelte、Mithril、Riot
  • Storybook 安装
    • 自动安装
      • npx -p @storybook/cli sb init --type vue
      • yarn add vue
      • yarn add vue-loader vue-template-compiler --dev
    • 手动安装
  1. Storybook下

  2. yarn workspaces

  • 开启yarn 的工作区 (npm 不支持,如果多个包引用相同的依赖,会自动把依赖的包提升到工作区的根目录的node_modules中)
  • 项目根目录的package.json
"private": true, // 作用是提交到github 或者 发布到npm的时候,禁止把当前根目录的内容提交
"workspaces": [
    "packages/*" // 管理所有包的路径
]
  • yarn workspaces 使用
    • 给工作区根目录安装开发依赖
      • yarn add jest -D -W
    • 给指定工作区安装依赖
      • yarn workspace lg-button(包名 在包的package.josn中) add lodash@4
    • 给所有的工作区安装依赖
      • yarn install
  1. Lerna上
  • Lerna 介绍
    • Lerna是一个优化使用git和npm管理多包仓库的工作流工具
    • 用于管理具有多个包的JavaScript项目
    • 它可以一键把代码提交到git和npm仓库
  • Larna 使用
    • 全局安装 yarn global add lerna
    • 初始化 lerna init
    • 发布 lerna publish
    • 配置发布命令 “scripts”: {“lerna”: “lerna publish”}
  • 在github上创建一个仓库
  • 必须保证npm 登陆,npm whoami 查看npm 登陆用户名
  • 发布前必须检查镜像地址是npm的 npm config get registry
  • yarn lerna
  1. Lerna下

  2. Vue组件的单元测试

  • 组件单元测试好处
    • 提供描述组件行为的文档
    • 节省手动测试的时间
    • 减少研发新特性时产生的bug
    • 改进设计
    • 促进重构
  • 安装依赖
    • Vue Test Utils vue官方提供的组件单元测试的官方库,需要结合一个单元测试框架一起使用
    • Jest feesbook出的,它和vue的结合比较方便,配置最少。jest可以进行单元测试,但并不支持单文件组件,需要一个预处理器,把单文件组件编译后的结果,也就是js代码交给jest处理
    • vue-jest vue官方提供的为jest服务的预处理器
    • babel-jest 测试文件中,会使用到ESM 语法,还有一些es新特性,需要降级处理
    • 安装 yarn add jest @vue/test-utils vue-jest babel-jest -D -W
    • 配置测试脚本
    "scripts": {
        "test": "jest"
    }
    
    • jest.config.js
    module.exports = {
      "testMatch": ["**/__tests__/**/*.[jt]s?(x)"],
      "moduleFileExtensions": [
        "js",
        "json",
        // 告诉 Jest 处理 `*.vue` 文件
        "vue"
      ],
      "transform": {
        // 用 `vue-jest` 处理 `*.vue` 文件
        ".*\\.(vue)$": "vue-jest",
        // 用 `babel-jest` 处理 js
        ".*\\.(js)$": "babel-jest" 
      }
    }
    
    • Babel 配置文件
    // babel.config.js
    module.exports = {
        presets: [
            [
                '@babel/preset-env'
            ]
        ]
    }
    
    • Babel 桥接: 处理找不到Babel的问题,vue-jest 依赖的是 babel@6 而最新是7
    • yarn add babel-core@bridge -D -W -D 开发依赖 -W安装在工作区根目录中
  1. Vue组件的单元测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-inS8QYQs-1627836997525)(./img/1627543570646.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYmg3AfF-1627836997528)(./img/1627543619996.jpg)]

  • 快照:第一次调用这个方法,会把测试内容存储到特定的文本文件中。第二次会把新生成的内容和前者进行对比,相同就显示成功
    • yarn test -u 会生成一个新的快照(对比基准)
  1. Rollup打包上
  • Rollup
    • Rollup 是一个模块打包器
    • Rollup 支持Tree-shaking
    • 打包的结果比Webpack更小
    • 开发框架/组件库的时候使用Rollup更合适
  • 安装依赖
    • rollup
    • rollup-plugin-terser 对代码进行压缩
    • rollup-plugin-vue@5.1.9 把单文件组件编译成js代码,一定要指定版本,最新版本是编译vue3
    • vue-template-compiler
    • rollup.config.js
    import { terser } from 'rollup-plugin-terser'
    import vue from 'rollup-plugin-vue'
    
    module.exports = [
      {
        input: 'index.js',
        output: [
          {
            file: 'dist/index.js',
            format: 'es'
          }
        ],
        plugins: [
          vue({
            css: true, // Dynamically inject css as a <style> tag
            compileTemplate: true, // Explicitly convert template to render function
          }),
          terser()
        ]
      }
    ]
    
    • 每个包package.json 配置
    "main": "dist/cjs/index.js",
    "module": "dist/es/index.js",
    
    • “scripts”: {“build”: “rollup -c”}
    • yarn workspace 包名 build 可以在根目录直接打包某个模块
  1. Rollup打包下
  • @rollup/plugin-json 可以让rollup把json文件作为模块加载 @rollup/plugin-node-resolve 作用是把依赖的第三方包打包进来
yarn add @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve -D -W
  • 项目根目录创建 rollup.config.js
import fs from 'fs'
import path from 'path'
import json from '@rollup/plugin-json'
import vue from 'rollup-plugin-vue'
import postcss from 'rollup-plugin-postcss'
import { terser } from 'rollup-plugin-terser'
import { nodeResolve } from '@rollup/plugin-node-resolve'

const isDev = process.env.NODE_ENV !== 'production' // 开发环境开启代码压缩

// 公共插件配置
const plugins = [
  vue({
    // Dynamically inject css as a <style> tag
    css: true,
    // Explicitly convert template to render function
    compileTemplate: true
  }),
  json(),
  nodeResolve(),
  postcss({
    // 把 css 插入到 style 中
    // inject: true,
    // 把 css 放到和js同一目录
    extract: true
  })
]

// 如果不是开发环境,开启压缩
isDev || plugins.push(terser())

// packages 文件夹路径
const root = path.resolve(__dirname, 'packages')

module.exports = fs.readdirSync(root)
  // 过滤,只保留文件夹
  .filter(item => fs.statSync(path.resolve(root, item)).isDirectory())
  // 为每一个文件夹创建对应的配置
  .map(item => {
    const pkg = require(path.resolve(root, item, 'package.json'))
    return {
      input: path.resolve(root, item, 'index.js'),
      output: [
        {
          exports: 'auto',
          file: path.resolve(root, item, pkg.main),
          format: 'cjs'
        },
        {
          exports: 'auto',
          file: path.join(root, item, pkg.module),
          format: 'es'
        },
      ],
      plugins: plugins
    }
  })
  • 在每一个包中设置 package.json 中的 main 和 module 字段
"main": "dist/cjs/index.js",
"module": "dist/es/index.js"
  • 根目录的 package.json 中配置 scripts
"build": "rollup -c"
  1. 设置环境变量
  • yarn add cross-env -D -W 可以跨平台设置环境变量
"build:prod": "cross-env NODE_ENV=production rollup -c",
"build:dev": "cross-env NODE_ENV=development rollup -c",
  1. 清理
  • “clean”: “lerna clean” 清理所有包中的node_modules
  • yarn add rimraf -D -W 删除指定文件夹
+ 在每一个包中设置 package.json 中的 "scripts": {"del": "rimraf dist" }
+ yarn workspaces run del
  1. 基于模板生成组件基本结构
  • 创建了Monorepo的项目结构,在一个项目中管理多个包,使用Storybook搭建项目,可以让用户快速预览组件,使用yarn workspaces管理所有包的依赖,使用Lerna发布项目,可以把每一个包发布到npm上。
  • yarn add plop -W -D
  • 添加模版
  • 添加配置
  • “script”: {“plop”: “plop”}
  1. 基于模板生成组件基本结构2
  • 实现了一个模版生成的link 组件
  1. 发布
  • yarn build:prod
  • npm whoami
  • yarn lerna
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值