使用Vite搭建官网

项目搭建

创建项目

cd /c/Users/11691/Desktop/x进入目录
yarn global add create-vite-app@1.18.0安装vite对应版本
cva roll_ui创建项目文件(cva是create-vite-app的简写)
cd roll_ui进入目录文件
yarnyarn install下载对应包

小知识

Vite的基本使用
vite 文档给出的命令是
npm init vite-app
yarn create vite-app
等价于
全局安装 create-vite-app 然后cva
等价于
npx createa-vite-app 即 npx 会帮你全局安装用到的包
注意项目名称最好用-连接,不要用驼峰命名

Vue 2 和 Vue 3 的区别
90% 的写法完全一致,除了以下几点
Vue 3 的 Template 支持多个根标签,Vue 2 不支持
Vue 3 有 createApp(),而 Vue 2 的是 new Vue()
Vue3是createApp(组件),而Vue2是 new Vue({template, render})
其他区别会在项目跟进中展开说明

初始化项目

项目文件目录介绍

index.html项目首页
image.png
script标签中type=”mudule”:

在script标签中写js代码,或者使用src引入js文件时,默认不能使用module形式,即不能使用import导入或导出文件,但是我们可以再script标签上加上type=module属性来改变方式。

main.js项目入口文件
image.png

APP.vue组件
image.png
提交代码
image.png
遇到问题
vscode源代码管理出现几千个更改
原因:
在桌面或者某个常用的文件夹里用git初始化命令,导致大量文件被追踪;
解决方法

  1. 找到被追踪的文件最顶层的文件夹;
  2. 删除.git文件夹;
  3. 重启Vs code。

    引入Vue Router

    专门和Vue3进行搭配的路由
    使用命令行查看 vue-router 所有版本号_npm info vue-router versions_
    安装vue-routeryarn add vue-router@4.0.0-beta.3

初始化vue-router

  1. 新建 history 对象
  2. 新建 router 对象
  3. 引入 TypeScript,重命名文件格式为ts
  4. vue-router的使用
    • app.use(router)
      • const app = createApp(App);app.mount('#app')创建并挂载根实例:app.use(router) ;
      • 通过调用 app.use(router),我们可以在任意组件中以 this.$router 的形式访问它,并且以 this.$route 的形式访问当前路由
    • 添加 占位(内容可变区)
    • 添加 链接进行单页面内路由跳转(类似a链接,内容不变区)
  1. //导入vue-router
  2. import { createWebHashHistory, createRouter } from 'vue-router'
  3. //创建vue-router需要用到的history对象和router对象
  4. const history = createWebHashHistory()
  5. const router = createRouter({
  6. history: history,
  7. routes: [
  8. { path: '/', component: HelloWorld }
  9. ]
  10. })
  11. //创建并挂载根实例,且使用use(router),确保整个应用支持路由。
  12. const app = createApp(App)
  13. app.use(router)
  14. app.mount('#app')
  15. //App组件内添加<router-view>和<router-link>

遇到问题:
TS文件无法理解导入的.vue文件,出现波浪线警告找不到模块“./components/HelloWorld.vue”或其相应的类型声明。
“Vue3 cant find module”
解决方法:
声明一个兼容vue的自定义.d.ts文件“shims-vue.d.ts”,放入源代码src目录中,因为其也属于源代码部分

  1. declare module '*.vue' {
  2. import { ComponentOptions } from "vue";
  3. const componentOptions: ComponentOptions
  4. export default componentOptions
  5. }

搭建首页和文档页html结构、添加样式

封装Topnav组件

跨平台中文字体解决方案

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

打包了一些常见字体的名字,希望能覆盖 Windows、Mac 以及 Linux 的常见字体,方便引用。

  1. font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
  2. //向右滑动查看更多

使用Provide和Inject实现切换功能

点击切换aside,一次显示,再点一次隐藏,使用provide/inject

补充:Vue3的ref引用

带 ref 的响应式变量

  1. setup意思是准备的意思,在props、data, methods和生命周期函数之前运行的,且setup只会在页面挂载组件的时候算一遍,这个方法中没法访问到this
  2. setup中使用vue3.0中提供的api(ref)来提供一个响应式的对象(和data中一样的响应式对象,data中直接返回一个对象),需要先引进,然后再使用,ref是一个函数,接收一个参数返回一个响应式的对象:在未来可以检测到改变,并且做出响应
  3. vue3.0的return 有意设计成这样,要精确控制哪些属性和方法被导出使用,可以更好的追踪引用和更新,在模板中想要使用,必须导出去

在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:

  1. import { ref } from 'vue'
  2. const counter = ref(0)

ref 为我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念。
一般用法:使用 ref 创建内部数据,类似之前的data

补充:Vue3的methods写法改变

①需要先声明,搭配箭头函数,一般写在setup中
②按需return

  1. //Vue2写法:
  2. methods {
  3. add() {
  4. data.age++
  5. }
  6. }
  7. //Vue3写法:
  8. setup() {
  9. ...
  10. const add = () => {
  11. data.age++
  12. }
  13. return {
  14. ...,
  15. add
  16. }
  17. }

手机页面适配

①首先考虑手机,再考虑pc
②手机适配界定范围为0-500px,大于500则为pc

③pc页面下的Doc页面下aside默认显示,手机页面下的Doc页面下的aside根据点击按钮进行切换显示与否
④判断用户浏览器页面宽度进行逻辑跳转

  1. const width = document.documentElement.clientWidth //获取用户浏览器页面宽度
  2. const menuVisible = ref(width <= 500 ? false : true) //逻辑跳转

同一页面内使用嵌套路由

①添加路由信息,在Doc页面内的子路由,需要在Doc路由中配置 children路由,同样是个数组,但path只写子路由名字
②添加路由显示占位,在Doc页面的main主内容区添加占位
③页面内路由切换后关闭aside
(如何关闭?
方法之一:router.afterEach()切换路由执行方法,在手机页面下切换Doc页面内的路由后关闭aside→缺点:路由切换的范围太大,应该针对某些路由切换进行方法更合适)

补充:afterEach()当路由切换后执行方法

afterEach
添加一个导航钩子,在每次导航后执行。返回一个删除注册钩子的函数。

封装一个单独的router.ts

便于全局使用和共享状态,而不受限于main.ts文件内

嵌套路由中设置二级组件的根

提供一个空的嵌套路径表示根,应用场景:展示默认页面,避免空白页面展示

  1. export const router = createRouter({
  2. history: history,
  3. routes: [
  4. { path: '/', component: Home },
  5. {
  6. path: '/doc', component: Doc, children: [
  7. { path: "", component: DocDemo }, //路径为空 即二级组件的根 展示Doc默认页面
  8. { path: "switch", component: SwitchDemo },
  9. { path: "button", component: ButtonDemo },
  10. { path: 'dialog', component: DialogDemo },
  11. { path: 'tabs', component: TabsDemo }
  12. ]
  13. }
  14. ]
  15. })

UI库中不能使用scoped

为了用户稳定使用自己的class

制作Switch组件

新建目录和文件:src——lib(存放所以组件)——Switch.vue
分别将组件导入components的各自Demo中以展示

整体步骤:

  1. 需求分析
  2. API设计(Switch组件怎么写)
  3. 写代码
    1. html
    2. css
    3. js
      1. 测试
      2. 改写
      3. 测试
      4. 改写

HTML骨架制作

CSS样式与动画

在scss中使用calc()

在 sass 中使用 calc,如果 calc 中包含一个变量,必须对这个变量使用sass的插值方法 : #{$variable}
正确的使用姿势是:

  1. $padding: 10px;
  2. $thirdWidth: 33.33333333%;
  3. .content{
  4. width: calc(#{$thirdWidth} - #{$padding});
  5. }

JS添加交互

点击switch后进行切换状态
①初始化状态:记录状态true还是false,即点击前为true,点击后为false,反之亦然

  1. setup() {
  2. const checked = ref(false); //使用setup初始化状态,一般声明变量用ref引用
  3. return { checked };
  4. },

②结合class绑定style实现点击切换效果

使用对象语法::class="{ checked }
点击button后进行切换效果: const toggle = () => {checked.value = !checked.value; };

遇到问题:
①外部父组件无法控制Switch组件内部初始状态value(如:value初始状态为true或false在Switch组件内存中已写入确定值)
②Switch组件内部状态value改变后外部组件无法获取其更新(如:Switch组件的状态改变后SwitchDemo无法获取)

解决方法:
①SwitchDemo中的添加value属性,Switch组件内部使用props,获取外部SwitchDemo传入的value状态
②SwitchDemo中的添加update:value事件,Switch组件内部value状态改变后触发事件给外部SwitchDemo
①和②结合起来的写法:父组件SwitchDemo中<Switch :value="y" @update:value="y = $event" />

  1. //Switch子组件内
  2. props: {
  3. value: Boolean, //用props接收外部父级组件传入的数据
  4. },
  5. setup(props, context) {
  6. const toggle = () => {
  7. context.emit("updat:evalue", !props.value);
  8. //context.emit()触发事件给外部父级组件,!props.value会被当做$event在父组件中使用
  9. };
  10. return { toggle };
  11. },

①和②可简化为Vue3的v-model写法:<Switch v-model:value="y"/>
去掉后半部分添加的updatevalue事件,改为v-model添加在绑定的value前即可

→知识点:Vue的双向绑定

vue的数据双向绑定主要通过Object.defineProperty()方法来进行数据劫持以及发布者-订阅模式来实现的
v-model其实体现的就是Vue的双向绑定,Vue3的v-model代替以前的v-model和.sync
双向绑定可以简单理解为自动监听,会自动监听update:value事件

补充:setup的参数用法

setup默认接收两个参数:props和context
props接收父级数据,只可读不可改写,
若需修改父级组件数据,则需通过context的emit()方法触发父级组件的事件,类似Vue2的this.$emit()用法

补充:scss嵌套小知识

scss使用嵌套关系,&的用法:
有空格的话不需要&,没有空格则需要&
有空格代表在标签内部,没有空格表示并列关系即同时存在的意思

制作Button组件

  1. 需求分析
  2. API设计(Button组件怎么写)实例如下:
  3. 写代码 ```javascript
  1. <a name="YngXw"></a>
  2. ### HTML骨架制作
  3. <a name="zJzTY"></a>
  4. #### 使用插槽slot
  5. 应用场景:向一个组件传递内容,如下所示:
  6. ```html
  7. <child>若子组件没有slot,则这句话不会显示,
  8. 为了将这句话显示,需在子组件模板中相应位置添加slot</child>

插槽在哪里显示子组件来进行确定,即子组件安装了一个坑,父组件有需要就可以在上面蹲坑

CSS样式与动画

Scss的颜色函数fade-out()

fade-out() 函数提升颜色的透明度,取值在 0 到 1 之间。
.xkd{ content:fade-out(rgba(100, 100, 255, 0.7), 0.1);}
编译成 CSS 代码:
.xkd { content: rgba(100, 100, 255, 0.6);}

Scss其他常用11个颜色函数:

函数 描述
rgb() 创建一个 Red-Green-Blue(RGB) 色
rgba() 创建一个带有透明度值的颜色
hsl() 通过色相、饱和度和亮度的值创建一个颜色
hsla() 通过色相、饱和度、亮度和透明的值创建一个颜色
red() 从一个颜色中获取其中红色值
lightness 获取一个颜色的亮度值(0% - 100%)
alpha 将颜色的 alpha 通道返回为介于 0 和 1 之间的数字
opacity 获取颜色透明度值(0-1)
mix() 把两种颜色混合起来
fade-in() 降低颜色的透明度,取值在 0-1 之。
fade-out() 提升颜色的透明度,取值在 0-1 之间。

UI库中不能使用scoped

因为 data-v-xxx 中的 xxx 每次运行可能不同必须输出稳定不变的 class 选择器,方便使用者覆盖

CSS最小影响原则

清楚你写的产品的定位是UI库,作为UI库,有些设定绝对不能影响库使用者,
比如有些样式用户可以自己定义修改,但作为有一些基础样式是不希望被用户覆盖的
在UI组件库文件夹中新建 组件的全局样式:libs——roll.scss,添加以组件特定前缀名开头的样式
当一些基础样式不希望被用户覆盖时,必须加前缀
.button 不行,很容易被使用者覆盖
.roll-button 可以,不太容易被覆盖
.theme-link 不行,很容易被使用者覆盖
.roll-theme-link 可以,不太容易被覆盖

补充:属性选择器匹配元素用法

  1. [class^="roll-"], //属性选择器匹配元素用法;匹配class以'roll-'开头的元素
  2. [class*=" roll-"] { //匹配class中包含' roll-'的元素
  3. margin: 0;
  4. padding: 0;
  5. box-sizing: border-box;
  6. font-size: 16px;
  7. font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
  8. }

JS添加交互

补充:attribute 和 property 的区别

两个单词的中文翻译也都非常相近(property:属性,attribute:特性),但实际上,二者是不同的东西,属于不同的范畴。

  • property是DOM中的属性,是JavaScript里的对象;
  • attribute是HTML标签上的特性,它的值只能够是字符串;

简单理解,Attribute就是dom节点自带的属性,例如html中常用的id、class、title、align等。
而Property是这个DOM元素作为对象,其附加的内容,例如childNodes、firstChild等。
为了更好的区分attribute和property,基本可以总结为attribute节点都是在HTML代码中可见的,而property只是一个普通的名值对属性。

禁用 Attribute 继承(Vue3属性绑定的规则细节)

当子组件为单个根节点时,父组件向子组件标签绑定的非 prop 的 attribute 将自动添加到子组件的根节点的 attribute 中。举例如下所示:

  1. //父组件中
  2. <template>
  3. <div class="father-wrapper">
  4. <h1>father component</h1>
  5. <ChildComp attribute1="hello"/> //父组件向子组件绑定非prop的attribute
  6. </div>
  7. </template>
  8. //子组件中:单个根节点为child-wrapper的div,则父组件传来的attribute会传到最根节点处
  9. <template>
  10. <div class="child-wrapper">
  11. <h1>child component</h1>
  12. <div class="child-div">
  13. 这是子组件中的div block
  14. </div>
  15. </div>
  16. </template>

遇到问题:
某些场景下,我们期望不让它继承到根节点的div.child-wrapper , 那该怎么办?
例如,我们需要加在 div.child-div上?
解决方法:
禁止继承

  1. export default {
  2. inheritAttrs: false,
  3. ...

②在所需的节点处添加v-bind="$attrs"批量绑定全部属性
在vue3中,可以通过$attrs或context.$attrs 获取到所有的 非prop的Attributes

  1. <template>
  2. <div class="child-wrapper">
  3. <h1>child component</h1>
  4. <div class="child-div" v-bind="$attrs"> <!-- <--这里 -->
  5. 这是子组件中的div block
  6. </div>
  7. </div>
  8. </template>

另外情况:在子组件中可指定attribute继承给哪个节点
若父组件传给子组件的非prop的attribute有多个,且需要将其传给子组件模板中的不同节点(两个或以上,下图示例两个),可利用剩余操作符将其分成可能不平均的两部分:

  1. //子组件中
  2. <template>
  3. <div :size = 'size'>
  4. <button v-bind = 'rest'>
  5. <slot />
  6. </button>
  7. </div>
  8. </template>
  9. <script lang = 'ts'>
  10. export default {
  11. inheritAttrs: false,
  12. setup(props,context) {
  13. const {size,...rest} = context.attrs //解构赋值+剩余操作符
  14. // ...rest表示:将剩余属性装进rest这个变量,rest为自定义变量名
  15. return {size,rest}
  16. }
  17. }
  18. </script>

ES6的剩余操作符…rest

在ES6中。 三个点(…) 有2个含义。分别表示 扩展运算符 和 剩余运算符
扩展运算符应用场景:

  • 数组展开
  • 将一个数组插入到另一个数据中
  • 字符串转数据

剩余运算符应用场景:

  • 获取不定数量的参数(可替代arguments)
  • 解构使用

扩展运算符和剩余运算符区别
简单地说,在某种程度上,剩余操作符和扩展运算符相反,扩展运算符会“展开”数组变成多个元素,剩余操作符会收集多个元素和“压缩”成一个单一的元素。

剩余操作符 和 arguments参数:
剩余操作符用于获取函数不定数量的参数数组,这个API是用来替代arguments的,arguments参数是一个类数组对象

  1. let a = (first, ...abc) => {
  2. console.log(first, abc); // 1 [2, 3, 4]
  3. };
  4. a(1, 2, 3, 4);

让Button绑定事件

@click
@focus
@mouseover

让Button绑定属性

代码片段示例:
image.png
props可静态传值也可动态传值
theme=’button’(默认) //button或者link等均为字符串,不需要通过v-bind绑定 (静态传值)

theme=’link’
theme=’text’

size=’big’
size=’small’

level=’main’
level=’danger’

:disabled = ‘true’ === disabled //true或false为布尔值,需要通过v-bind绑定,引号内为JS内容(动态传值)
:disbaled = ‘false’(不传则默认)

:loading = ‘true’ ===loading //true或false为布尔值,需要通过v-bind绑定,引号内为JS内容(动态传值)
:loading = ‘false’(不传则默认)

绑定class的对象语法

动态切换多个 class,可以与普通class共存
示例:
:class="{[theme-${theme}]: theme}"
vue会根据键值是否为 Truthy 来自动的让某个class生效
→只有八个Falsy值(false,0,-0,0n,””, ‘’, ``, null, undefined,NaN),其他均为truthy值

Button的class使用计算属性

class原本使用属性选择器进行匹配,存在多个class比较复杂,可使用计算属性,在setup中添加计算属性,需从Vue3引入
ts中:
computed是一个函数,参数是一个回调函数,这个回调中可以处理想返回的值,
返回的是一个只读的响应式引用
computed是一个类似ref对象的一种数据类型,也可以直接在模板中使用

  1. setup() {
  2. ...
  3. const classes = computed(() => {
  4. return {
  5. [`gulu-theme-${theme}`]: theme,
  6. [`gulu-size-${size}`]: size,
  7. };
  8. });
  9. }

vue3 组件传值之 props 与 attrs 的区别

$attrs 属性可以看做 props 的加强版,用来简化 vue 组件传值

  1. props 要先声明才能取值,attrs 不用先声明
  2. props 声明过的属性,attrs 里不会再出现

  3. props 不包含事件,attrs 包含事件

  4. props 支持 string 以外的类型,attrs 只有 string 类型

制作Dialog组件

需求分析
API设计

  1. <Dialog
  2. visible
  3. title="标题"
  4. @yes="fn1" @no="fn2"
  5. ></Dialog>

写代码

HTML骨架制作

CSS样式与动画

JS添加交互

让Dialog绑定visible,结合点击事件

:visible=”x”
x为响应式引用的变量,通过点击Button切换展示Dialog与否

  1. setup() {
  2. const x = ref(false); // 声明x为布尔的引用值
  3. const toggle = () => {
  4. x.value = !x.value; //通过点击按钮实现切换展示与否的效果(结合点击事件)
  5. };
  6. return { x, toggle };
  7. },

让Dialog支持关闭

共有四处支持Dialog关闭
①×
②外部黑色遮罩
③OK
④Cancel

注意:
遇到问题:
增加场景:用户不希望点击黑色遮罩层就关闭Dialog,则需要提供一个开放接口给用户
解决方法:
Dialog上绑定:closeOnClickOverlay动态属性,默认值为true

遇到问题:
增加场景:Dialog对话框需要用户添加内容后点击OK才能关闭Dialog,否则关闭不了,则需要在OK事件中添加判断机制,判断emit返回值是否有内容,但是!
emit()是没有返回值的,即默认返回undefined,因为事件是没有返回值,这是事件的特点
解决方法:
使用普通函数即可,函数有返回值,
通过在子组件上绑定函数,子组件接收props为函数类型,子组件再判断父组件中定义的函数的返回值,根据这个返回值做关闭操作与否

补充:ES6可选链操作符

  1. props.ok && props.ok() ==== props.ok?.()
  2. //如果props.ok存在则执行;
  3. //如果props.ok不存在则直接返回undefined

如果给定的函数不存在为undefined或null,则返回 undefined。

补充:vue3具名插槽

遇到问题:
此时Dialog支持由父组件DialogDemo传递进来的title和content
但title是由prop传递进来且由{{}}展示的,类型声明只支持String字符串
而content由传递进来,支持HTML标签显示(即支持加粗等语法功能)
那怎样让title也由slot传递进来,支持HTML标签呢?
解决方法:
使用Vue3具名插槽,支持同一模板渲染展示多个slot,让Dialog支持自定义title和content

应用场景:
当需要同一模板渲染多个插槽时, 元素有一个特殊的 attribute:name。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方

基本使用:
外部父组件蹲坑,