Vue3官网实现(总结)

1,159 阅读2分钟

一、技术细节

  • 使用Vue3 Components API 和Typescript制作了四个组件 。
  • 官网代码支持高亮显示。
  • 展示折叠组件封装。
  • 使用 Vite 的 dev 命令和 build 命令进行开发与部署
  • 尝试使用rollup进行项目库打包,并发布npm包。
  • 尝试使用shell脚本实现自动化部署

二、成果展示

此处为GitHub源码地址

点击此处效果预览

1.首页 image.png

2.介绍 image.png

3.switch image.png

4.button image.png

image.png

5.modal

image.png

6.tab image.png

三:学后总结

1.setup(compoents ApI)完成一次传值

//子组件
<script lang="ts">
export default {
  props: {
    value: Boolean, //接收父组件的值
  },
  setup(props, context) {
    const toggle = () => {
      context.emit("update:value", !props.value); //触发事件后值会返回给父组件
    };
    return { toggle };//必须return 不然组件无法拿到toggle
  }
};
</script>
//父组件
<script lang="ts">
import Switch from '../lib/Switch.vue' //导入组件
import { ref} from 'vue'
export default {
  components: {Switch},//接收组件
  setup() {
    const bool = ref(false)//父组件要传给子组件的值,默认为false
    return {
      bool //将默认值Return
    }
  }
}
</script>

2.ref(使变量另行具备响应式)

import {ref} from 'vue'
const value = ref<boolean>(false)
return {
    value
}

3.v-model

<switch :value='x' @update:value="x=$event"/>
  
<switch v-model:value="y"/>

4.vue-router

//全局安装vue-router后,在router.ts这个入口文件里面创建 history和router
import {createRouter, createWebHashHistory} from 'vue-router';

const history = createWebHashHistory() //创建 history
export const router = createRouter({ //创建 router
  history: history,
  routes: [
    {path: '/', component: Home},
    {path: '/doc', component: Doc,children:[//嵌套路由
        {path:'',redirect:'/doc/Intro'},
        {path:'intro',component:Intro},
      ]}
  ]
})
添加<router-view>//要告诉app组件,路由的组件内容在哪里显示
添加<router-link>//跳转页面

5.用 provide 和 inject 实现切换功能

//父组件做标记
import {ref,provide} from 'vue'
export default {
  name: 'App',
  setup(){
    const width = document.documentElement.clientWidth;//获取当前屏幕宽度
    const menuVisible = ref(width > 500);
    provide('menuVisible',menuVisible)//父组件做标记
  }
}
</script>
//子组件获取数据
import {inject,Ref} from 'vue'
setup(){
   const menuVisible = inject<Ref<boolean>>('menuVisible')// inject接收值
 }

6.inheritAttrs:false(组件内部不在继承调用时传来的属性)

属性继承(继承到根元素): 父组件button的属性会直接继承在子组件的button上的最外层div,就意味你在父组件上面写的事件也直接会直接继承在子组件的最外层

//如何继承到子组件的button上(批量绑定属性)
//子组件里面写上继承属性false(inheritAttrs:false)
<script lang="ts">
export default {
    inheritAttrs:false
}  
</script>

//让div里的button(子组件)绑定$attrs(这个绑定的属性是写在父组件里面的所有属性)
<div>
<button v-bind = "$attrs"></button>
</div>
//如何分两部分继承呢?(一部分属性继承在div上,一部分继承在button上)
//子组件
<template>
  <div :size="size">//继承在div
    <Button v-bind="rest"></Button>//继承在button
  </div>
</template>

<script lang="ts">
export default {
   inheritAttrs:false,
   setup(props,context){
   const {size,...rest} = context.attrs//从context.attrs里面拿到两部份值
   return {size,rest}//return出去
   }
}
</script>

7.具名插槽

//父组件
<template v-slot:context>
<div>
哈喽 我是你爸爸
</div>
</template>


<template v-slot:title>
<strong>
我是一个标题
</strong>
</template>
//子组件
<slot name="title"/>

<main>
<slot name="context"/>
</main>

8.Teleport

(任意传送门Teleport 是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术)

//直接将这一块传送到Dom节点里面的body
<Teleport to = "body">
<div>
一大块代码
</div>
</Teleport>

9.createApp

使用createApp这个 API返回一个应用实例,并且可以通过链条的方式继续调用其他的方法)

image.png

10.如何确认子组件类型(防御性编程)检查context.slots.default()数组

image.png

image.png

image.png

11.ui库不能使用scoped ,每一个class 必须添加前缀,css最小影响原则

12.一些用到的钩子

onMounted  //挂载后做的操作,只会在第一次挂载渲染
onUpdated  //第一次渲染后的每次更新做的操作
watchEffect //上面两个的结合,相当于监听,只要有变化就运行

13.TypeScript泛型

const indicator = ref<HTMLDivElement>(null) //null类型

14.获取宽高和位置的API(el.getBoundingClientRect())

const {width,height,top,left} = el.getBoundingClientRect()//获取el元素的宽高等

15.ES6析构赋值的重命名语法

const {left:left1} = x.getBoundingClientRect()
const {left:left2} = y.getBoundingClientRect()

16.引入Github的Markdown

markdown GITHub地址

yarn add github-markdown-css //安装
import 'github-markdown-css' //在mian.ts引入,就可以直接使用

<template>
  <article class="markdown-body" >//加入class="markdown-body"就可以
   <h1>安装</h1>
   <p>打开终端运行下面命令</P>
  </article>
</template>

效果:

image.png

17.如何显示源码 使用vue-loader的Custom Blocks

<1>.在vite.config.ts文件里面添加一些配置

<2>.抽离用户需要拷贝的代码(子组件),并且在子组件里面写入一个<dem>标签,比如(<dem>支持隐藏</dem>)

<3>.然后在父组件里面引用,作为组件在components:{}引用一次,还得作为变量在setup(){}里面return出来一次,然后在父组件的<pre>标签里面使用。

比如:<pre>{{Switch1Demo.__demo}}</pre>

就可以显示出子组件里面除了<dem>的所有源代码

//vite.config.ts文件里面添加一些配置

// @ts-nocheck


import { md } from "./plugins/md";
import fs from 'fs'
import {baseParse} from '@vue/compiler-core'

export default {
  base:'./',
  assetsDir:'assets',
  plugins: [md()],
  vueCustomBlockTransforms: {
    demo: (options) => { //组件含有demo标签的话会做以下操作
      const { code, path } = options
      const file = fs.readFileSync(path).toString()
      const parsed = baseParse(file).children.find(n => n.tag === 'demo')
      const title = parsed.children[0].content
      const main = file.split(parsed.loc.source).join('').trim()
      return `export default function (Component) {
        Component.__sourceCode = ${//将组件除了<dem>标签外的所有源代码放在组件的__sourceCode里面 (下划线是为了隐藏起来)
        JSON.stringify(main) //源代码
      }
        Component.__sourceCodeTitle = ${JSON.stringify(title)}
      }`.trim()
    }
  }
};

18.如何高亮源码 使用 prismjs库 和 vue的 v-html

1.prismjs官网

2.我使用的是node.js,所以使用文档中node的方法

3.准备工作

yarn add prismjs //安装这个库
import  'prismjs' //在使用源码的地方引入
import  'prismjs/themes/prism.css'  //引入css,跟prism.css同级的CSS是其他主体,也可以试试,比如prism.css改成prism-okaidia.css等
console.log(window.Prism) //可以log出啦,但是会webstorm会在Prism下面报错,解决方法往下

const Prism = (window as any).Prism //这样Prism就是any类型,就可以随意使用,临时方法,有更好的可以自己使用

//记得在setup() {return Prism }

  1. 看文档如何使用

image.png

//使用例子
<pre class="language-html" 
v-html="Prism.highlight(props.component.__sourceCode, Prism.languages.html, 'html'" /> 

//class="language-html"是代码的背景

题外话:

import prismjs from 'prismjs'; 这种引入方法会报错(没有默认的导出),因为这个库比较老,不支持这种引入方法。只能看源代码,看源码的方法,node_modules < prismjs < package.json < "main"(入口文件是prism.js) < prism.js < 里面就是所有的源码,主要看导出了什么,搜索下export, 发现并没有导出 ,只用了node.js的模块声明方法。再往下看就看到了“global.Prism = Prism”,全局变量。懂了!global就是window 使用全局引入import 'prismjs'

image.png

19.用到的一些网页链接

借鉴Web布局的网址

调节渐变色生成css代码

Fonts.css -- 跨平台中文字体解决方案