首页
前端面试题
前端报错总结
电子书
更多
插件下载
Search
1
JavaScript基础(二)操作符 流程控制
42 阅读
2
HTML基础
20 阅读
3
Vue基础
17 阅读
4
wctype.h
14 阅读
5
Vue2(知识点)
13 阅读
默认分类
HTML CSS
HTML基础
CSS
HTML5 CSS3
javaScript
javaScript基础
javaScript高级
Web APIs
jQuery
js小总结
WEB开发布局
Vue
PS切图
数据可视化
Git使用
Uniapp
c语言入门
标准库
嵌入式
登录
Search
liuxiaobai
累计撰写
108
篇文章
累计收到
12
条评论
首页
栏目
默认分类
HTML CSS
HTML基础
CSS
HTML5 CSS3
javaScript
javaScript基础
javaScript高级
Web APIs
jQuery
js小总结
WEB开发布局
Vue
PS切图
数据可视化
Git使用
Uniapp
c语言入门
标准库
嵌入式
页面
前端面试题
前端报错总结
电子书
插件下载
搜索到
104
篇与
的结果
2023-01-05
flex
㈠ 标题布局题目:标题居中且超长打点,标题左右包含若干图标。图片解答:⑴ 标题居中,必须控制左右距离相等,即典型的左中右结构。图片常见做法是使用绝对布局,标题左右设置相同边距(margin 或 padding),左右图标使用绝对布局(position: absolute)盖在标题左右边距上。需要注意的是标题容器需要设置为相对布局(position: relative),作为左右图标的锚点。图片更优雅的做法是通过 flexbox 布局,左右固定宽度,剩余空间都是中间的,即弹性伸缩属性设置为自动(flex: auto),等同于 flex: 1 1 auto。其中 flex-grow 为 1 意味着空间富余可以扩展,flex-shrink 为 1 意味着空间不够可以收缩,flex-basis 为 auto 意味着使用 width 或 height 显示设置的宽高(主轴方向横向则取宽度,纵向则取高度),没有设置则使用内容的宽高。图片⑵ 标题超长打点,需要设置不换行(white-space: nowrap)、溢出隐藏(overflow: hidden) 和文本溢出打点(text-overflow: ellipsis)。⑶ 左右图标布局,常见的做法准确计算边距使得刚好左侧左对齐,右侧右对齐。但更优雅的做法还是 flexbox 布局,把计算的事“甩”给弹性布局,直接左侧主轴方向正方向(flex-direction: row,默认值),右侧主轴方向反方向(flex-direction: row-reverse)。图片㈡ 左右布局题目:左侧若干图标从左到右依次排放,最后一个图标靠最右侧放置。图片解答:有三种解题方法,除了上面的绝对布局和弹性伸缩属性设置为自动外,还更简单的方案,就是在flexbox布局中设置左边为自动(margin-left: auto)。图片㈢ 均分布局题目:不同内容宽度的子元素均分空间(下图绿蓝红为子元素,其中深色为内容区,浅色为扩展富余空间)。解答:众所周知,自动弹性伸缩(flex: auto)只能扩展富余空间而非整体空间,上题我们很容易且好像也只能做到如下效果:图片均分空间做不到?答案必须是“No”。flex: auto 全称是 flex: 1 1 auto,其中最后一位是 flex-basis,即弹性基准大小,默认是内容大小。那么内容大小能不能设置为0?答案曰之“能”。直接上代码:图片㈣ 跟随布局题目:图标始终紧紧跟随在标题后面,当标题内容超长时,图标仍然显示,标题文本可以超长打点。解答:这问题也太水了吧?整个标题设置为弹性布局,文本伸缩属性使用默认值(flex: initial,即 flex: 0 1 auto,空间富余不伸展但空间不足要收缩),设置超长打点(overflow: hidden; text-overflow: ellipsis;),图标放标题右侧即可。图片这里多说一句,细心的小伙伴发现,上面超长打点没有设置不换行(white-space: nowrap)。为啥超长了不是另起一行?因为div标签默认块布局(display: block),本身就是不换行的,如果布局属性换成了弹性布局,则需要像标题布局一样设置不换行。等等,这道题“水”怎么说?之所以有这道题,是因为我延用了 Android 的布局思维(我从 Android 转 Web 不久),标题跟随不截断在 App 中首页和列表页随处可见,自然 Android 也需要实现该功能。但 Android 里面如果要超长打点,就必须限定宽度,常规写法是文本宽度为0,自适应占用其他控件布局后剩下的富余空间(LinearLayout# layout_width: 0; layout_weight: 1; ),因为 Android 支持的布局限制,使得该题变得有点意思了。如果带入到前端,假设伸缩属性只能设置为即伸展又收缩(flex: auto),如何实现标题跟随不截断?先直接改下上面代码看看具象效果。那么,问题来了?在文本未超长时,图标不跟随了...让我们看看 Android 上面的效果,下面图示红框圈起来的 2 个区域,区域 1 和上面 Web 实现类似,文本控件宽度为 0 权重为 1(android:layout_width="0px" android:layout_weight="1"),在文本较短时直接扩展了富余空间撑大了,导致图标右对齐而不是跟随。图片这里用到一个“小妙招”,文本控件属性不变,但是文本和图标控件再包一层横向线性布局自适应宽度(android:layout_width="wrap_content"),这样就能确保文本较短时不会撑大。因为父控件为自适应宽度,没有额外的富余空间且最大空间受自己父控件约束,效果即区域 2。上述思路来源于原美团同事袁件在 2015 年的分享,这个解题思路之所以念念不忘,主要是有点像脑筋急转弯,记住了也就会了,要是硬想可能很难想出来。当然了,一般这种情况通过自定义布局就能简单实现,主旨就是先算其他控件大小,如果当前宽度过长则只占据剩下空间,否则正常自身宽度。同样方法照搬到 Web 是否可以?让我们来试试,文本控件自动伸缩,Android 设置属性为 layout_width="0px" 和 layout_weight="1",等同于 Web 设置为 flex: auto。在文本和图标外包了一层自适应宽度的横向线性布局,Android 设置属性为 layout_width="wrap_content",等同于 Web 设置为(width: 100%; max-width: min-content; )或(width: min-content; max-width: 100%;),即宽度为内容大小和父组件大小中最小值。划重点,通过同时设置 width 和 max-width 使得宽度在短内容时跟随,在长内容时打点,本身也是“小妙招”,正常很少两个同时使用。认同的小伙伴有必要记一下。为啥我绕这么大的圈子讲一个 Web 上舍易求难的问题,主要是通过 Web 实践检验已有的 Android 经验,逻辑大同小异必然实现殊途同归,同时多场景对比,加强自己对弹性布局的理解。㈣ 父子宽度约束探究题目:父组件宽度固定(width: 200px),组件设置为弹性布局(display: flex),里面子组件是文本和图标。图片在文本长度超出时,组件宽度是超长的内容宽度(下图中标号 1)还是父组件宽度(下图中标号 2)?解答:结论先行,实测答案时标号 2。咋一看,弹性组件容器没有设置宽度,感觉其宽度应该就是内容宽度,既然内容超长,那么背景色肯定和内容一样。既然和想的不一样,查一下 CSS 官方文档,width 不显示设置时使用默认值 auto,auto 在文档中的解释是“浏览器将会为指定的元素计算并选择一个宽度。”不过感觉说了和没说一样,到底是怎么一个计算算法也没有讲。实测发现 auto 对应的是父组件大小,即背景色覆盖区域只是父组件范围。那么如果我们要做到组件宽度是超长内容宽度呢?这是一道送分题,直接设置宽度为内容大小(width: min-content)即可。不过我在实测中发现,还有另外一个方法,将父组件设置为弹性布局也能达到同样效果。逻辑上说,不显示设置宽度则取默认值 auto,即父组件大小,父组件不管是否是弹性布局,大小已经显示指定了,但实际影响了组件宽度?至于为什么是这样,现在的我的确不知道,问题留给未来的自己,或者屏幕前的你来回答。㈤ 空间无限缩小好奇心来自于看到官方文档 《flex 布局的基本概念》中的“如果有太多元素超出容器,它们会溢出而不会换行。”太多不是会压缩么,对于弹性默认属性(flex: initial,即 flex: 0 1 auto,不扩展只收缩),怎么会是溢出?我自己复制黏贴了一堆文本,发现竟然真是溢出?如下图。给我整不会了...后来在和同事永健的沟通下,得知当组件宽度被收缩到最小内容大小时就不再收缩,文本本身大小即最小内容大小,可以通过最小宽度(min-width)设置内容大小。至此,真相终于大白了。顺便吹毛求疵一下,官方文档写得还是不严谨,引起了不必要的误解。附 Web Demo Html 源码:<meta charset="utf-8"> <title>flex Demo</title> <style type="text/css"> body { font-size: 32px; }<!-- 标题栏布局 --> <div style="width: 640px; height: 100px; display: flex; align-items: center; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; display: flex; padding: 0 15px;"> <div> ? </div> </div> <div style="flex: auto; margin: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> 标题标题标题标题标题标题标题标题标题</div> <div style="width: 100px; display: flex; flex-direction: row-reverse; justify-content: space-between; padding: 0 15px;"> <div>...</div> <div>?</div> </div> </div> <!-- 绝对定位方式实现左中右布局 --> <div style="width: 640px; height: 100px; position: relative; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; height: 100px; position: absolute; top: 0; left: 0; background-color: #96D149;"></div> <div style="height: 100px; padding: 0 100px; background-color: #5976B3;"></div> <div style="width: 100px; height: 100px; position: absolute; top: 0; right: 0; background-color: #eb7b88;"> </div> </div> <!-- flex布局伸缩属性设置为自动实现左中右布局 --> <div style="width: 640px; height: 100px; display: flex; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; background-color: #96D149;"></div> <div style="flex: auto; background-color: #5976B3;"></div> <div style="width: 100px; background-color: #eb7b88;"> </div> </div> <!-- ⑴ 绝对定位方式实现左右布局--> <div style="width: 640px; height: 100px; position: relative; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; height: 100px; display: inline-block; line-height: 100px; text-align: center;">? </div> <div style="width: 100px; height: 100px; display: inline-block; line-height: 100px; text-align: center;">? </div> <div style="width: 100px; height: 100px; display: inline-block; line-height: 100px; text-align: center;">? </div> <div style="width: 100px; height: 100px; position: absolute; top: 0; right: 0; display: inline-block; line-height: 100px; text-align: center;"> ?</div> </div> <!-- ⑵ flex布局伸缩属性设置为自动实现左右布局--> <div style="width: 640px; height: 100px; display: flex; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">? </div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">? </div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">? </div> <!-- 空div延长可用空间使得最后一个元素靠右--> <div style="flex: auto;"></div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;"> ?</div> </div> <!-- ⑶ flex布局左边距设置为自动实现左右布局--> <div style="width: 640px; height: 100px; display: flex; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">?</div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">?</div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center;">?</div> <div style="width: 100px; height: 100px; line-height: 100px; text-align: center; margin-left: auto;">?</div> </div> <!-- 均分布局设置 flex: 1 0 0 实现不同内容宽度子元素均分空间 --> <div style="width: 640px; height: 100px; display: flex; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="flex: 1 0 0; background-color: #96D149; margin: 5px;"> <div style="width: 50px; line-height: 90px; background-color: #729d39; text-align: center;"> 1</div> </div> <div style="flex: 1 0 0; background-color: #5976B3; margin: 5px;"> <div style="width: 100px; line-height: 90px; background-color: #486092; text-align: center;"> 2</div> </div> <div style="flex: 1 0 0; background-color: #eb7b88; margin: 5px;"> <div style="width: 150px; line-height: 90px; background-color: #a8545e; text-align: center;"> 3</div> </div> </div> <!-- 设置flex: auto 只能均分富余空间 --> <div style="width: 640px; height: 100px; display: flex; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="flex: auto; background-color: #96D149; margin: 5px;"> <div style="width: 50px; line-height: 90px; background-color: #729d39; text-align: center;"> 1</div> </div> <div style="flex: auto; background-color: #5976B3; margin: 5px;"> <div style="width: 100px; line-height: 90px; background-color: #486092; text-align: center;"> 2</div> </div> <div style="flex: auto; background-color: #eb7b88; margin: 5px;"> <div style="width: 150px; line-height: 90px; background-color: #a8545e; text-align: center;"> 3</div> </div> </div> <!-- 图标跟随且不截断布局 flex: initial,即 flex: 0 1 auto --> <div style="width: 640px; height: 100px; display: flex; align-items: center; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <div style="flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; margin: 5px;">1234567890</div> <span>?</span> <span style="margin: 5px;">?</span> </div> <div style="width: 640px; height: 100px; display: flex; align-items: center; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <span style="flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; margin-left: 10px;"> 1234567890123456789012345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> <!-- 图标跟随且不截断布局:仅伸展不收缩(flex: auto) --> <div style="width: 640px; height: 100px; display: flex; align-items: center; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <span style="flex: auto; overflow: hidden; text-overflow: ellipsis; margin: 5px;">1234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> <div style="width: 640px; height: 100px; display: flex; align-items: center; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <span style="flex: auto; overflow: hidden; text-overflow: ellipsis; margin-left: 10px;"> 1234567890123456789012345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> <!-- 图标跟随且不截断布局:仿 Android 思路,除文本控件自动伸缩(layout_width="0px" layout_weight="1")外,还在文本和图标外包了一层自适应宽度的横向线性布局(layout_width="wrap_content") --> <div style="width: 640px; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <!-- 在文本和图标外包了一层自适应宽度的横向线性布局((width: 100%; max-width: min-content;) 或者 (width: min-content; max-width: 100%;)) --> <!-- <div style="width: 100%; max-width: min-content; line-height: 100px; display: flex; align-items: center;"></div> --> <div style="width: min-content; max-width: 100%; line-height: 100px; display: flex; align-items: center;"> <!-- 文本控件自动伸缩(flex: auto) --> <span style="flex: auto; overflow: hidden; text-overflow: ellipsis; margin: 5px;">1234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> </div> <div style="width: 640px; margin: 30px; border: 3px solid black; background-color: #fed9a1;"> <!-- 在文本和图标外包了一层自适应宽度的横向线性布局((width: 100%; max-width: min-content;) 或者 (width: min-content; max-width: 100%;)) --> <!-- <div style="width: 100%; max-width: min-content; line-height: 100px; display: flex; align-items: center;"></div> --> <div style="width: min-content; max-width: 100%; line-height: 100px; display: flex; align-items: center;"> <!-- 文本控件自动伸缩(flex: auto) --> <span style="flex: auto; overflow: hidden; text-overflow: ellipsis; margin: 5px;"> 1234567890123456789012345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> </div> <!-- 父子宽度约束探究:未显示设置弹性布局宽度时,弹性布局宽度等于内容宽度还是父组件宽度?--> <!-- 父组件固定宽度 --> <div style="width: 200px; height: 100px; background-color: #fed9a1; border: 3px solid black; align-items: center; margin: 30px;"> <!-- 组件弹性布局显示设置宽度为内容宽度 --> <div style="display: flex; width: min-content; height: 100px; line-height: 100px; background-color: #F2DE5C; align-items: center;"> <!-- 子组件文案超长 --> <span style="margin: 5px;">12345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> </div> <!-- 父组件固定宽度且设置为弹性容器 --> <div style="width: 200px; height: 100px; display: flex; background-color: #fed9a1; border: 3px solid black; align-items: center; margin: 30px;"> <!-- 组件弹性布局不显示设置宽度 --> <div style="display: flex; height: 100px; line-height: 100px; background-color: #F2DE5C; align-items: center;"> <!-- 子组件文案超长 --> <span style="margin: 5px;">12345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> </div> <div style="width: 200px; height: 100px; background-color: #fed9a1; border: 3px solid black; align-items: center; margin:30px"> <div style="display: flex; height: 100px; line-height: 100px; background-color: #F2DE5C; align-items: center;"> <span style="margin: 5px;">12345678901234567890</span> <span>?</span> <span style="margin: 5px;">?</span> </div> </div></html附 Android Demo XML 源码:<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="match_parent"> <!--整体显示区域,纵向线性布局,相对于父控件水平居中--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginTop="100px" android:layout_gravity="center_horizontal"> <!--区域1短文本,仅文本控件自动伸缩(layout_width="0px" layout_weight="1")--> <LinearLayout android:layout_width="640px" android:layout_height="100px" android:orientation="horizontal" android:gravity="center_vertical" android:background="@drawable/border_background"> <TextView android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:singleLine="true" android:ellipsize="marquee" android:text="1234567890"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:text="?"/> </LinearLayout> <!--区域1长文本,仅文本控件自动伸缩(layout_width="0px" layout_weight="1")--> <LinearLayout android:layout_width="640px" android:layout_height="100px" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginTop="30px" android:background="@drawable/border_background"> <TextView android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:singleLine="true" android:ellipsize="marquee" android:text="1234567890123456789012345678901234567890"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:text="?"/> </LinearLayout> <!--区域2短文本,除文本控件自动伸缩外,还在文本和图标外包了一层自适应宽度的横向线性布局(layout_width="wrap_content")--> <LinearLayout android:layout_width="640px" android:layout_height="100px" android:orientation="horizontal" android:layout_marginTop="100px" android:background="@drawable/border_background"> <!--巧妙在于自适应宽度的包裹,完美消除了权重撑大问题--> <LinearLayout android:layout_width="wrap_content" android:layout_height="100px" android:orientation="horizontal" android:gravity="center_vertical"> <TextView android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:singleLine="true" android:ellipsize="marquee" android:text="1234567890"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:text="?"/> </LinearLayout> </LinearLayout> <!--区域2长文本,除文本控件自动伸缩外,还在文本和图标外包了一层自适应宽度的横向线性布局(layout_width="wrap_content")--> <LinearLayout android:layout_width="640px" android:layout_height="100px" android:orientation="horizontal" android:layout_marginTop="30px" android:background="@drawable/border_background"> <!--巧妙在于自适应宽度的包裹,完美消除了权重撑大问题--> <LinearLayout android:layout_width="wrap_content" android:layout_height="100px" android:orientation="horizontal" android:gravity="center_vertical"> <TextView android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" android:text="1234567890123456789012345678901234567890" android:layout_marginLeft="5px" android:layout_marginRight="5px"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5px" android:layout_marginRight="5px" android:text="?"/> </LinearLayout> </LinearLayout> </LinearLayout>
2023年01月05日
6 阅读
0 评论
0 点赞
2022-09-14
UniAPP 快速入门学习
一、UniAPP 介绍(1)什么是 UniAPP ?uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到 iOS、Android、H5,以及各种小程序(微信/支付宝/百度/头条/ QQ /钉钉)等多个平台,方便开发者快速交付,不需要转换开发思维,不需要更改开发习惯。(2)为什么要选择 UniAPP ?开发者/案例数量更多几十万应用、uni 统计月活12亿、70+ 微信 / QQ群平台能力不受限在跨端的同时,通过条件编译 + 平台特有 API 调用,可以优雅的为某平台写个性化代码,调用专有能力而不影响其他平台性能体验优秀加载新页面速度更快、自动 diff 更新数据,App 端支持原生渲染支撑更流畅的用户体验,小程序端的性能优于市场其他框架周边生态丰富插件市场数千款插件,支持 NPM、支持小程序组件和SDK,微信生态的各种 sdk 可直接用于跨平台 APP学习成本低基于通用的前端技术栈,采用 vue 语法+微信小程序 api,无额外学习成本(3)UniAPP 功能框架(4)UniAPP 开发环境搭建下载开发工具 HBuilderXHBuilderX 是通用的前端开发工具,但为uni-app做了特别强化。下载 App 开发版,可开箱即用;如下载标准版,在运行或发行uni-app时,会提示安装uni-app插件,插件下载完成后方可使用创建 uni-app 项目选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。uni-app自带的模板有 Hello uni-app ,是官方的组件和API示例。还有一个重要模板是 uni ui项目模板,日常开发推荐使用该模板,已内置大量常用组件。运行 uni-app主要包括:浏览器运行、真机运行、小程序运行等发布 uni-app主要包括:云端原生 APP 、离线原生 APP、H5、各种小程序二、UniAPP 初始化相关配置(1)工程目录结构┌─components uni-app组件目录 │ └─comp-a.vue 可复用的a组件 ├─hybrid 存放本地网页的目录(自建) ├─platforms 存放各平台专用页面的目录(自建) ├─pages 业务页面文件存放的目录 │ ├─index │ │ └─index.vue index页面 │ └─list │ └─list.vue list页面 ├─static 存放应用引用静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此 ├─wxcomponents 存放小程序组件的目录(自建) ├─common 公共资源(自建) ├─api 请求封装(自建) ├─store 状态管理(自建) ├─main.js Vue初始化入口文件 ├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期 ├─manifest.json 配置应用名称、appid、logo、版本等打包信息 └─pages.json 配置页面路由、导航条、选项卡等页面类信息 提示static下目录的 js 文件不会被 webpack 编译,里面如果有 es6 的代码,不经过转换直接运行,在手机设备上会报错。所以 less、scss 等资源同样不要放在 static 目录下,建议这些公共的资源放在 common目录下(2)应用配置 manifest.jsonmanifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等,我们也可以在这里为 Vue 为H5 设置跨域拦截处理器(3)编译配置 vue.config.jsvue.config.js 是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置 webpack 等编译选项。官方文档(4)全局配置 page.jsonpages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。它类似微信小程序中app.json的页面管理部分。官方文档属性类型必填描述globalStyleObject否设置默认页面的窗口表现pagesObject Array是设置页面路径及窗口表现easycomObject否组件自动引入规则tabBarObject否设置底部 tab 的表现conditionObject否启动模式配置subPackagesObject Array否分包加载配置preloadRuleObject否分包预下载规则(5)全局样式 uni.scssuni.scss文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss文件里预置了一批scss变量预置。官方文档uni-app 官方扩展插件(uni ui)及 插件市场 上很多三方插件均使用了这些样式变量,如果你是插件开发者,建议你使用 scss 预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App。uni.scss是一个特殊文件,在代码中无需 import 这个文件即可在scss代码中使用这里的样式变量。uni-app的编译器在webpack配置中特殊处理了这个 uni.scss,使得每个 scss 文件都被注入这个uni.scss,达到全局可用的效果。如果开发者想要less、stylus的全局使用,需要在vue.config.js中自行配置webpack策略。(6)主组件 App.vueApp.vue是uni-app的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件。但App.vue本身不是页面,这里不能编写视图元素。这个文件的作用包括:调用应用生命周期函数、配置全局样式、配置全局的存储globalData应用生命周期仅可在App.vue中监听,在页面监听无效。(7)入口文件 main.jsmain.js是uni-app的入口文件,主要作用是初始化vue实例、定义全局组件、使用需要的插件如vuex。官方文档(8)UniAPP 开发规范及资源路径开发规范约定页面文件向导 Vue单文件组件(SFC)规范组件标签靠近小程序规范,详见 uni-app 组件规范互连能力(JS API)靠近微信小程序规范,但需要将替换替换 wx 为 uni ,详见uni-app接口规范数据绑定及事件处理同 Vue.js 规范,同时补充了 App 和页面的生命周期为兼容多端运行,建议使用 flex 布局进行开发资源路径说明template 内约会静态资源,如 image,video 等标签的 src 属性时,可以使用相对路径或绝对路径,形式如下:<!-- 绝对路径,/static指根目录下的static目录,在cli项目中/static指src目录下的static目录 --> <image class="logo" src="/static/logo.png"></image> <image class="logo" src="@/static/logo.png"></image> <!-- 相对路径 --> <image class="logo" src="../../static/logo.png"></image>注意@ 初始的绝对路径以及相对路径会通过 base64 转换规则校验约会的静态资源在非 h5 平台,均不转为 base64H5平台,小于4kb的资源会被转换成base64,其余不转js 文件或 script 标签内,可以使用相对路径和绝对路径,形式如下:// 绝对路径,@指向项目根目录,在cli项目中@指向src目录 import add from '@/common/add.js' // 相对路径 import add from '../../common/add.js'css 文件或 style 标签内,可以使用相对路径和绝对路径,形式如下:/* 绝对路径 */ @import url('/common/uni.css'); @import url('@/common/uni.css'); /* 相对路径 */ @import url('../../common/uni.css');css 文件或 style 标签内引用的图片路径,可以使用相对路径也可以使用绝对路径,形式如下:/* 绝对路径 */ background-image: url(/static/logo.png); background-image: url(@/static/logo.png); /* 相对路径 */ background-image: url(../../static/logo.png);三、UniAPP 生命周期学习一个工具的目的核心是什么?是为了解决核心业务逻辑问题,业务逻辑很多时候简单的解释一句话:“在合适的时机干合适的事情”,OK!什么是合适的时机呢?简单的说,页面运行过程中,各个阶段的回调函数就是页面中的时机,我们也叫这个为“生命周期钩子函数”,当然,业务中我们也会写到很多「回调」的逻辑,这些回调其实也是咱们自定义的时机,UniAPP 的生命周期钩子函数回调函数有哪些呢?我们来理解一下!uni-app 完整支持 Vue 实例的生命周期,同时还新增 应用生命周期 及 页面生命周期。(1)应用生命周期函数名说明onLaunch当uni-app 初始化完成时触发(全局只触发一次)onShow当 uni-app 启动,或从后台进入前台显示onHide当 uni-app 从前台进入后台onError当 uni-app 报错时触发onUniNViewMessage对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯onUnhandledRejection对未处理的 Promise 拒绝事件监听函数(2.8.1+)onPageNotFound页面不存在监听函数onThemeChange监听系统主题变化(2)页面生命周期函数名说明onLoad监听页面加载,其参数为上个页面传递的数据,参数类型为Object(用于页面传参),参考示例onShow监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面onReady监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发onHide监听页面隐藏onUnload监听页面卸载onResize监听窗口尺寸变化onPullDownRefresh监听用户下拉动作,一般用于下拉刷新,参考示例onReachBottom页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。具体见下方注意事项onTabItemTap点击 tab 时触发,参数为Object,具体见下方注意事项onShareAppMessage用户点击右上角分享onPageScroll监听页面滚动,参数为ObjectonNavigationBarButtonTap监听原生标题栏按钮点击事件,参数为ObjectonBackPress监听页面返回onNavigationBarSearchInputChanged监听原生标题栏搜索输入框输入内容变化事件onNavigationBarSearchInputConfirmed监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。onNavigationBarSearchInputClicked监听原生标题栏搜索输入框点击事件onShareTimeline监听用户点击右上角转发到朋友圈onAddToFavorites监听用户点击右上角收藏四、UniAPP 路由配置及页面跳转(1)路由配置uni-app 页面路由全部交给框架统一管理,开发者需要在pages.json里配置每个路由页面的路径及页面样式(类似小程序在 app.json 中配置页面路由)。"pages": [ { "path": "pages/index", "style": { "navigationBarTitleText": "路由配置", "navigationBarBackgroundColor": "#FFFFFF", "navigationBarTextStyle": "black", "backgroundColor": "#FFFFFF", "enablePullDownRefresh": true } }, { "path": "pages/user", "style": { "navigationBarTitleText": "路由配置", "navigationBarBackgroundColor": "#FFFFFF", "navigationBarTextStyle": "black", "backgroundColor": "#FFFFFF", "enablePullDownRefresh": true } } ](2)路由跳转uni-app 有两种页面路由跳转方式:使用navigator组件跳转(标签式导航)、调用API跳转(编程式导航)框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下:路由方式页面栈表现触发时机初始化新页面入栈uni-app 打开的第一个页面打开新页面新页面入栈调用 API uni.navigateTo、使用组件 <navigator open-type="navigate" />页面重定向当前页面出栈,新页面入栈调用 API uni.redirectTo 、使用组件页面返回页面不断出栈,直到目标返回页调用 API uni.navigateBack 、使用组件 、用户按左上角返回按钮、安卓用户点击物理back按键Tab 切换页面全部出栈,只留下新的 Tab 页面调用 API uni.switchTab 、使用组件 、用户切换 Tab重加载页面全部出栈,只留下新的页面调用 API uni.reLaunch 、使用组件(3)获取当前页面栈getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。注意: getCurrentPages() 仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。(4)路由传参与接收说明:页面生命周期的 onLoad()监听页面加载,其参数为上个页面传递的数据,如://页面跳转并传递参数 uni.navigateTo({ url: 'page2?name=liy&message=Hello' });url为将要跳转的页面路径 ,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔。如 'path?key1=value2&key2=value2',path为下一个页面的路径,下一个页面的onLoad函数可得到传递的参数。// 页面 2 接收参数 onLoad: function (option) { //option为object类型,会序列化上个页面传递的参数 console.log(option.name); //打印出上个页面传递的参数。 console.log(option.message); //打印出上个页面传递的参数。 }注意:url 有长度限制,太长的字符串会传递失败,并且不规范的字符格式也可能导致传递失败,所以对于复杂参数建议使用 encodeURI、decodeURI 进行处理后传递(5)小程序路由分包配置因小程序有体积和资源加载限制,各家小程序平台提供了分包方式,优化小程序的下载和启动速度。所谓的主包,即放置默认启动页面及 TabBar 页面,而分包则是根据 pages.json 的配置进行划分。在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示,此时终端界面会有等待提示。"subPackages": [ { "root": "news", "pages": [{ "path": "index", "style": { "navigationBarTitleText": "新闻中心", "navigationBarBackgroundColor": "#FFFFFF", "navigationBarTextStyle": "black", "backgroundColor": "#FFFFFF" } } ] } ... ], // 预下载分包设置 "preloadRule": { "pages/index": { "network": "all", "packages": ["activities"] } }五、UniAPP 常用组件简介uni-app 为开发者提供了一系列基础组件,类似 HTML 里的基础标签元素,但 uni-app 的组件与 HTML 不同,而是与小程序相同,更适合手机端使用。虽然不推荐使用 HTML 标签,但实际上如果开发者写了div等标签,在编译到非H5平台时也会被编译器转换为 view 标签,类似的还有 span 转 text、a 转navigator等,包括 css 里的元素选择器也会转,但为了管理方便、策略统一,新写代码时仍然建议使用view等组件。开发者可以通过组合这些基础组件进行快速开发, 基于内置的基础组件,可以开发各种扩展组件,组件规范与vue组件相同。案例:知心姐姐布局实现六、UniAPP 常用 API 简介uni-app的 js 代码,h5 端运行于浏览器中,非 h5 端 Android 平台运行在 v8 引擎中,iOS 平台运行在 iOS 自带的 jscore 引擎中。所以,uni-app的 jsAPI 由标准 ECMAScript 的 js API 和 uni 扩展 API 这两部分组成。ECMAScript 由 Ecma 国际管理,是基础 js 语法。浏览器基于标准 js 扩充了window、document 等 js API;Node.js 基于标准 js 扩充了 fs 等模块;小程序也基于标准 js 扩展了各种 wx.xx、my.xx、swan.xx 的 API。标准 ecmascript 的 API 非常多,比如:console、settimeout等等。非 H5 端,虽然不支持 window、document、navigator 等浏览器的 js API,但也支持标准 ECMAScript。开发者不要把浏览器里的 js 等价于标准 js。所以 uni-app 的非 H5 端,一样支持标准 js,支持 if、for 等语法,支持字符串、数组、时间等变量及各种处理方法,仅仅是不支持浏览器专用对象。案例:知心姐姐聊天功能七、UniAPP 自定义组件与通信(1)自定义组件概念组件是 vue 技术中非常重要的部分,组件使得与ui相关的轮子可以方便的制造和共享,进而使得vue使用者的开发效率大幅提升,在项目的 component 目录下存放组件,uni-app 只支持 vue 单文件组件(.vue 组件)组件可以使用「全局注册」和「页面引入」两种方式进行使用,使用分为三步:导入 import xxx from 'xxx'注册 Vue.use('xx',xx) components:{ xxx }使用 <xx />(2)父子组件通信父组件通过自定义属性向子组件传递数据子组件通过 props 接收父组件传递的数据父组件通过自定义事件标签向子组件传递事件子组件通过触发父组件定义事件方式修改父组件数据(3)slot 数据分发与作用域插槽父组件通过调用子组件内部嵌套 html 内容作为 slot 分发给子组件子组件通过在 slot 标签上添加属性,向父组件通信数据,作用域插槽(4)全局事件定义及通信在整个应用的任何地方均可以使用 uni.$on 创建一个全局事件在整个应用的任何地方也均可以使用 uni.$emit 来触发全局事件,实现多组件见的数据通信八、UniAPP Vuex 状态管理概念Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。应用场景Vue多个组件之间需要共享数据或状态。关键规则State:存储状态数据Getter:从状态数据派生数据,相当于 State 的计算属性Mutation:存储用于同步更改状态数据的方法,默认传入的参数为 stateAction:存储用于异步更改状态数据,但不是直接更改,而是通过触发 Mutation 方法实现,默认参数为contextModule:Vuex 模块化交互关系使用方式import { mapState, mapActions } from 'vuex' export default { computed: { ...mapState(['loginState', 'userInfo']) }, methods: { ...mapActions(['userLoginAction', 'userLogoutAction']), } }体验案例:模拟用户登陆逻辑实现注意:配合使用 Storage 来实现刷新页面后状态持续保持的业务需求九、运行环境判断与跨端兼容(1)开发环境和生产环境uni-app 可通过 process.env.NODE_ENV 判断当前环境是开发环境还是生产环境,一般用于连接测试服务器或生产服务器的动态切换。在HBuilderX 中,点击「运行」编译出来的代码是开发环境,点击「发行」编译出来的代码是生产环境if(process.env.NODE_ENV === 'development'){ console.log('开发环境') }else{ console.log('生产环境') }(2)判断平台平台判断有2种场景,一种是在编译期判断,一种是在运行期判断。编译期判断编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码, // #ifdef H5 alert("只有h5平台才有alert方法") // #endif // 如上代码只会编译到H5的发行包里,其他平台的包不会包含如上代码。运行期判断 运行期判断是指代码已经打入包中,仍然需要在运行期判断平台,此时可使用 uni.getSystemInfoSync().platform 判断客户端环境是 Android、iOS 还是小程序开发工具 switch(uni.getSystemInfoSync().platform){ case 'android': console.log('运行Android上') break; case 'ios': console.log('运行iOS上') break; default: console.log('运行在开发者工具上') break; }(3)跨端兼容uni-app 已将常用的组件、JS API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足,但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。大量写 if else,会造成代码执行性能低下和管理混乱。编译到不同的工程后二次修改,会让后续升级变的很麻烦。在 C 语言中,通过 #ifdef、#ifndef 的方式,为 windows、mac 等不同 os 编译不同的代码。 uni-app 参考这个思路,为 uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。\#ifdef:if defined 仅在某平台存在\#ifndef:if not defined 除了某平台均存在%PLATFORM%:平台名称%PLATFORM% 可取值如下:值平台APP-PLUSAppAPP-PLUS-NVUEApp nvueH5H5MP-WEIXIN微信小程序MP-ALIPAY支付宝小程序MP-BAIDU百度小程序MP-TOUTIAO字节跳动小程序MP-QQQQ小程序MP-360360小程序MP微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序/360小程序QUICKAPP-WEBVIEW快应用通用(包含联盟、华为)QUICKAPP-WEBVIEW-UNION快应用联盟QUICKAPP-WEBVIEW-HUAWEI快应用华为
2022年09月14日
8 阅读
0 评论
0 点赞
2022-09-09
Vue3快速上手
Vue3快速上手1.Vue3简介2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王)耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者github上的tags地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.02.Vue3带来了什么1.性能的提升打包大小减少41%初次渲染快55%, 更新渲染快133%内存减少54%......2.源码的升级使用Proxy代替defineProperty实现响应式重写虚拟DOM的实现和Tree-Shaking......3.拥抱TypeScriptVue3可以更好的支持TypeScript4.新的特性Composition API(组合API)setup配置ref与reactivewatch与watchEffectprovide与inject......新的内置组件FragmentTeleportSuspense其他改变新的生命周期钩子data 选项应始终被声明为一个函数移除keyCode支持作为 v-on 的修饰符......一、创建Vue3.0工程1.使用 vue-cli 创建官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的@vue/cli npm install -g @vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run serve2.使用 vite 创建官方文档:https://v3.cn.vuejs.org/guide/installation.html#vitevite官网:https://vitejs.cn什么是vite?—— 新一代前端构建工具。优势如下:开发环境中,无需打包操作,可快速的冷启动。轻量快速的热重载(HMR)。真正的按需编译,不再等待整个应用编译完成。传统构建 与 vite构建对比图## 创建工程 npm init vite-app <project-name> ## 进入工程目录 cd <project-name> ## 安装依赖 npm install ## 运行 npm run dev二、常用 Composition API官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html1.拉开序幕的setup理解:Vue3.0中一个新的配置项,值为一个函数。setup是所有Composition API(组合API)“ 表演的舞台 ”。组件中所用到的:数据、方法等等,均要配置在setup中。setup函数的两种返回值:若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)若返回一个渲染函数:则可以自定义渲染内容。(了解)注意点:尽量不要与Vue2.x配置混用Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。但在setup中不能访问到Vue2.x配置(data、methos、computed...)。如果有重名, setup优先。setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)2.ref函数作用: 定义一个响应式的数据语法: const xxx = ref(initValue)创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。JS中操作数据: xxx.value模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>备注:接收的数据可以是:基本类型、也可以是对象类型。基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的。对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。3.reactive函数作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)reactive定义的响应式数据是“深层次的”。内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。4.Vue3.0中的响应式原理vue2.x的响应式实现原理:对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。Object.defineProperty(data, 'count', { get () {}, set () {} })存在问题:新增属性、删除属性, 界面不会更新。直接通过下标修改数组, 界面不会自动更新。Vue3.0的响应式实现原理:通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。通过Reflect(反射): 对源对象的属性进行操作。MDN文档中描述的Proxy与Reflect:Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ProxyReflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflectnew Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom' 5.reactive对比ref从定义数据角度对比:ref用来定义:基本类型数据。reactive用来定义:对象(或数组)类型数据。备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。从原理角度对比:ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。从使用角度对比:ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。reactive定义的数据:操作数据与读取数据:均不需要.value。6.setup的两个注意点setup执行的时机在beforeCreate之前执行一次,this是undefined。setup的参数props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。context:上下文对象attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。slots: 收到的插槽内容, 相当于 this.$slots。emit: 分发自定义事件的函数, 相当于 this.$emit。7.计算属性与监视1.computed函数与Vue2.x中computed配置功能一致写法import {computed} from 'vue' setup(){ ... //计算属性——简写 let fullName = computed(()=>{ return person.firstName + '-' + person.lastName }) //计算属性——完整 let fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) }2.watch函数与Vue2.x中watch配置功能一致两个小“坑”:监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。监视reactive定义的响应式数据中某个属性时:deep配置有效。//情况一:监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) },{immediate:true}) //情况二:监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{ console.log('sum或msg变化了',newValue,oldValue) }) /* 情况三:监视reactive定义的响应式数据 若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!! 若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //此处的deep配置不再奏效 //情况四:监视reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //情况五:监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //特殊情况 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效3.watchEffect函数watch的套路是:既要指明监视的属性,也要指明监视的回调。watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。watchEffect有点像computed:但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 watchEffect(()=>{ const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回调执行了') })8.生命周期vue2.x的生命周期vue3.0的生命周期Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:beforeDestroy改名为 beforeUnmountdestroyed改名为 unmountedVue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:beforeCreate===>setup()created=======>setup()beforeMount ===>onBeforeMountmounted=======>onMountedbeforeUpdate===>onBeforeUpdateupdated =======>onUpdatedbeforeUnmount ==>onBeforeUnmountunmounted =====>onUnmounted9.自定义hook函数什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。类似于vue2.x中的mixin。自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。10.toRef作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。语法:const name = toRef(person,'name')应用: 要将响应式对象中的某个属性单独提供给外部使用时。扩展:toRefs 与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)三、其它 Composition API1.shallowReactive 与 shallowRefshallowReactive:只处理对象最外层属性的响应式(浅响应式)。shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。什么时候使用?如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。2.readonly 与 shallowReadonlyreadonly: 让一个响应式数据变为只读的(深只读)。shallowReadonly:让一个响应式数据变为只读的(浅只读)。应用场景: 不希望数据被修改时。3.toRaw 与 markRawtoRaw:作用:将一个由reactive生成的响应式对象转为普通对象。使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。markRaw:作用:标记一个对象,使其永远不会再成为响应式对象。应用场景:有些值不应被设置为响应式的,例如复杂的第三方类库等。当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。4.customRef作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。实现防抖效果:<template> <input type="text" v-model="keyword"> <h3>{{keyword}}</h3> </template> <script> import {ref,customRef} from 'vue' export default { name:'Demo', setup(){ // let keyword = ref('hello') //使用Vue准备好的内置ref //自定义一个myRef function myRef(value,delay){ let timer //通过customRef去实现自定义 return customRef((track,trigger)=>{ return{ get(){ track() //告诉Vue这个value值是需要被“追踪”的 return value }, set(newValue){ clearTimeout(timer) timer = setTimeout(()=>{ value = newValue trigger() //告诉Vue去更新界面 },delay) } } }) } let keyword = myRef('hello',500) //使用程序员自定义的ref return { keyword } } } </script>5.provide 与 inject作用:实现祖与后代组件间通信套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据具体写法:祖组件中:setup(){ ...... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ...... }后代组件中:setup(props,context){ ...... const car = inject('car') return {car} ...... }6.响应式数据的判断isRef: 检查一个值是否为一个 ref 对象isReactive: 检查一个对象是否是由 reactive 创建的响应式代理isReadonly: 检查一个对象是否是由 readonly 创建的只读代理isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理四、Composition API 的优势1.Options API 存在的问题使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。 2.Composition API 的优势我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。 五、新的组件1.Fragment在Vue2中: 组件必须有一个根标签在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中好处: 减少标签层级, 减小内存占用2.Teleport什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。<teleport to="移动位置"> <div v-if="isShow" class="mask"> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div> </teleport>3.Suspense等待异步组件时渲染一些额外内容,让应用有更好的用户体验使用步骤:异步引入组件import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))使用Suspense包裹组件,并配置好default 与 fallback<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中.....</h3> </template> </Suspense> </div> </template>六、其他1.全局API的转移Vue 2.x 有许多全局 API 和配置。例如:注册全局组件、注册全局指令等。//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }Vue3.0中对这些API做出了调整:将全局的API,即:Vue.xxx调整到应用实例(app)上2.x 全局 API(Vue)3.x 实例 API (app)Vue.config.xxxxapp.config.xxxxVue.config.productionTip移除Vue.componentapp.componentVue.directiveapp.directiveVue.mixinapp.mixinVue.useapp.useVue.prototypeapp.config.globalProperties2.其他改变data选项应始终被声明为一个函数。过度类名的更改:Vue2.x写法.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }Vue3.x写法.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes移除v-on.native修饰符父组件中绑定事件<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />子组件中声明自定义事件<script> export default { emits: ['close'] } </script>移除过滤器(filter)过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。......
2022年09月09日
7 阅读
0 评论
0 点赞
1
...
18
19
20
...
35