1-14day
1.全局注册
html代码:
<div id="example">
<my-component></my-component>
</div>
JS代码:
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
var vm = new Vue({
el: '#example',
data: {
}
})
渲染结果为:
<div id="example">
<div>A custom component!</div>
</div>
或者另外一种注册方式,通过 全局API:Vue.extend()
代码如下:
// 注册
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
});
// 注册
Vue.component('my-component', MyComponent);
var vm = new Vue({
el: '#example',
data: {
}
})
[](javascript:void(0)😉
Vue.extend()使用说明
下面说明下Vue.extend( options )
的使用。
参数:{Object} options
用法:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象
。
data
选项是特例,需要注意 - 在 Vue.extend()
中它必须是函数
。
<div id="mount-point"></div>
[](javascript:void(0)😉
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
Vue 的 m o u n t ( ) 为 手 动 挂 载 , 在 项 目 中 可 用 于 延 时 挂 载 ( 例 如 在 挂 载 之 前 要 进 行 一 些 其 他 操 作 、 判 断 等 ) , 之 后 要 手 动 挂 载 上 。 n e w V u e 时 , e l 和 mount()为手动挂载,在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作、判断等),之后要手动挂载上。new Vue时,el和 mount()为手动挂载,在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作、判断等),之后要手动挂载上。newVue时,el和mount并没有本质上的不同。
结果如下:
<p>Walter White aka Heisenberg</p>
这个步骤就是生命周期的beforemount,mount
自定义事件
我们知道,父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。
使用 v-on 绑定自定义事件
每个 Vue 实例都实现了事件接口,即:
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
Vue 的事件系统与浏览器的
EventTarget API
有所不同。尽管它们的运行起来类似,但是$on
和$emit
并不是addEventListener
和dispatchEvent
的别名。
另外,父组件可以在使用子组件的地方直接用 v-on
来监听子组件触发的事件。
不能用 $on 侦听子组件释放的事件,而必须在模板里直接用 v-on 绑定,参见下面的例子。
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
[](javascript:void(0)😉
Vue.component('button-counter', {
template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
组件化开发
我们在mui-ui组件网站使用的组件,也是开发人员开发的组件,例如:mui-button组件就是一个vue组件,我们在这里就可以看见了
就是按需导入:(选择整个组件文件,并选择导入Button)
import { Button } from 'mint-ui';
Vue.use(Button)
全局导入:
import Mint from 'mint-ui'
import 'mint-ui/lib/style.css'
Vue.use(Mint)
Vue.use()可以和vue一起加载,随时使用
import axios from 'axios'
const Axios = {
install (Vue) {
const result = axios.create({ baseURL: 'http://tpadmin.test/api/' })
Vue.prototype.$http = result
}
}
export default Axios
install是必须的,换成test()就会不识别
把axios注册成原型对象,使用的时候很方便
视频:https://www.bilibili.com/video/BV1FS4y1d7q9?from=search&seid=918740444835597733&spm_id_from=333.337.0.0
文档:https://blog.csdn.net/github_37516320/article/details/78321391
axios注册
- created(){}方法里面尽量放methods的方法,
1-15日
api文档
position:relative;是相对定位,是相对于自己的定位,需要使用left top bottom right
position:absole是绝对定位,上级元素有绝对定位,相对的定位,就以上一个定位方式为标准
-
img的width,height大小,可以压缩图片,
-
上一级div对图片img限制的用:overflow:hidden;进行裁剪图片
-
api文档实例的 登录接口
- 请求路径:login //这里使用.post(login)
- 请求方法:post
- 请求参数
.post(login,this.login),所以post()的第一个参数是请求地址,第二个是传递参数(一整个参数传过去) - 但是传递的参数有要求,必须要使用下面的字段,否则无法识别
参数名 参数说明 备注 username 用户名 password 密码 - 响应参数
参数名 参数说明 备注 code 状态码 1登录成功 0登录失败 msg 提示信息 url data 登录人员信息 - 响应数据
下面就是result.data请求之后的数据,就使用需要使用判断code的值是多少,然后获取失败的话就用this.$toast(result.data.msg)弹出消息
{ "code": 1, "msg": "登录成功", "data": { "session_id": "akemvcqn21dscu5bupg5sn5p7t", //注意,登录时将会额外得到一个session_id的数据 "id": 1, "username": "test" }, "url": "http://localhost:10086/", "wait": 3 }
vuex
$store.getters[‘user/isLogin’]
$store.state.user.username
this.$store.commit(‘user/setUser’, result.data.data)
1-16日
1.session
Session:记录一系列状态,例如:这样服务器才知道你在下一个页面用不用重新登录,服务器才知道你当前的状态,更好的推送东西,这是与cookies的区别之一
Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。
解释session:当访问服务器否个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session
原理:***HTTP协议是非连接性的,取完当前浏览器的内容,然后关闭浏览器后,链接就断开了,而没有任何机制去记录取出后的信息。***而当需要访问同一个网站的另外一个页面时(就好比如在第一个页面选择购买的商品后,跳转到第二个页面去进行付款)这个时候取出来的信息,就读不出来了。所以必须要有一种机制让页面知道原理页面的session内容。
问题:如何知道浏览器和这个服务器中的session是一一对应的呢?又如何保证不会去访问其它的session呢?
原理解答:**就是当访问一个页面的时候给浏览器创建一个独一无二的号码,也给同时创建的session赋予同样的号码。**这样就可以在打开同一个网站的第二个页面时获取到第一个页面中session保留下来的对应信息(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session)。这个号码也叫sessionID,session的ID号码,session的独一无二号码。(就是发送过去session_id,把他放在请求头中发送给服务器)
2.登录验证
目的是为了:1.让服务器认识多个请求都是一个客户发出来的,在服务器的多个网页下保存状态.2.保存住客户的状态
Http协议是无状态的,同一个客户多次访问服务器,服务器无法识别同一个客户的关联请求,也无法保存客户的状态。要解决这个问题,服务器需要获取到客户的身份,客户需要在每次发起Http请求的时候携带相应的身份信息,服务端获取到这个身份信息以后再返回相应的资源。我们在使用Web系统时,提供用户名和密码的过程就是向客户端提供身份认证信息的过程,那么在做接口测试的时候如何模拟用户登录过程,在每一次的接口请求中携带客户的身份信息呢?
在解决这个问题以前我们首先要了解常用的Http认证授权技术:
基于表单的认证(Cookie & Session):基于表单的认证并不是在HTTP协议中定义的,而是服务器自己实现的认证方式,安全程度取决于实现程度。一般用Cookie来管理Session会话,是最常用的认证方式之一。它的安全程度取决于服务器的实现程度,客户端在Cookie中携带认证信息,服务器解析并返回结果。
基于JWT(Json Web Token)的认证:App和服务端常用的认证方式,用户ID和密码传输到服务器上验证,服务器验证通过以后生成加密的JWT Token返回给客户端,客户端再发起请求时携带返回的Token进行认证。
Http Basic认证:最早的Http认证方式,用户ID和密码以分号连接,经过Base64编码后存储到Authorization字段,发送到服务端进行认证 ;用户ID/密码以明文形式暴露在网络上,安全性较差。
Http Digest认证:在HttpBasic的基础上,进行了一些安全性的改造,用户ID, 密码 , 服务器/客户端随机数,域,请求信息,经过MD5加密后存储到Authorization字段,发送到服务端进行认证;密码经过MD5加密,安全性比Basic略高。
其他认证方式(Oauth认证,单点登陆,HMAC认证):通过特定的加密字段和加密流程,对客户端和服务端的信息进行加密生成认证字段,放在Authorization或者是消息体里来实现客户信息的认证
除了上述四种,还有很多其他的认证方式,包括现在很流行的单点登录。但无论哪一种认证方式,Http都是无状态的,绕过登录的核心都是在Http请求中携带认证信息。这个认证信息我们可以通过和开发同学沟通签名加密的流程和字段,在客户端通过编码方式实现;也可以通过抓包工具抓取到相应的认证字段,然后通过Jmeter组件把字段添加到发起的Http请求当中。
3.区别:通过存储方式及类型区别
- 存储位置 : vuex存储在内存,而session则以文件的方式存储在本地;
- 应用场景 : vuex用于组件间的传值,而session则用于页面间的传值;
- 时效性 : vuex存储的值刷新时会丢失,而localstorage不会清除,除非手动删除;
4.接口文档设计技巧
登录接口
- 请求路径:login
- 请求方法:post
- 请求参数
参数名 | 参数说明 | 备注 |
---|---|---|
username | 用户名 | |
password | 密码 |
- 响应参数
参数名 | 参数说明 | 备注 |
---|---|---|
code | 状态码 | 1登录成功 0登录失败 |
msg | 提示信息 | |
url | ||
data | 登录人员信息 |
- 响应数据
{
"code": 1,
"msg": "登录成功",
"data": {
"session_id": "akemvcqn21dscu5bupg5sn5p7t", //注意,登录时将会额外得到一个session_id的数据
"id": 1,
"username": "test"
},
"url": "http://localhost:10086/",
"wait": 3
}
个人经验:
- 设置session_id是为了和浏览器沟通,
- id使用来在vuex判断是否登录了,然后给页面判断登录前和登录后的页面.
- username用户们用来显示用户名数据,也可以继续设置显示其他的
5. axios的拦截器(Interceptors)
axios 的拦截器:interceptors
如果我们想在请求之前做点什么,用拦截器再好不过了
拦截器一般做什么?
1. 修改请求头的一些配置项
2. 给请求的过程添加一些请求的图标
3. 给请求添加参数
总之就是调整请求头里面的数据
const res = Vue.prototype.$auth
const result = axios.create({ baseURL: 'http://tpadmin.test/api/' })
result.interceptors.request.use((config) => {
config.headers.Authorization = res.getAuthorization()
return config
})
Vue.prototype.$http = result
6.localStorage的使用
localStorage的方法是直接操作浏览器本地储存的数据
- localstorage.getItem()取的是key的名字
- localstorage.setItem(key:value)设置,默认是token,还有这个办法localStorage.removeItem(‘token’)
getAuthorization () {
return localStorage.getItem('Authorization')
},
setAuthorization (Authorization) {
localStorage.setItem('Authorization', Authorization)
}
提交的请求可以先让他先加载一下,this.$indicator.open({‘text’:…})
1-17day
1.display:flex
两个div块级元素在一起,
肯定是一个在上面,一个在下面布局.(flex可以控制左右,上下排序)
为了解决这种情况可以使用弹性盒子布局,这样就有一个在右边一个在左边
https://www.cnblogs.com/hellocd/p/10443237.html
-
flex:1会根据剩下的宽度自动排行.
-
flex-flow: row wrap;第一个是排列方式,第二个是换行
-
.menu { display: flex; position: absolute; text-align: center; top: 40px; bottom: 50px; width: 100%; overflow: hidden; // 这个oberflow是为在左边可以一直显示菜单的 }
-
这个是没有高度的,所以需要在下面的菜单要加position定位元素,不要遮住
-
li加了高度后,可以在里面的文字加行高
-
flex: 1;
// 剩下的宽度都是由这个分配
4.初始化的方法开头用 _initxxxxx()方法
data(){
return{
imgList:[]
}
},
wacth:{
// wacth里面的数据是函数形式
imgList(){
this.$nextTick(()=>{
})
}
}
2.滚动组件
5.1.1 better-scroll滚动组件
为了在Vue中实现左右菜单联动的滚动效果,需要借助better-scroll滚动组件来完成。
在项目中使用如下命令安装better-scroll插件。
npm install better-scroll@1.15.2 --save
安装后,打开src\pages\Category.vue文件,导入better-scroll插件。
<script>
import BScroll from 'better-scroll'
</script>
better-scroll插件需要操作DOM,因此要确保在menus数据加载完成后,并且已经在页面中显示出来以后,再来初始化better-scroll。所以,可以利用watch监听menu的数据变动,一旦发生变动,则页面也会发生变化。
为了确保DOM渲染完成后,再初始化better-scroll,在这里需要使用this.$nextTick异步函数,示例代码如下。
watch: {
menus () {
// $nextTick用来在下次DOM更新循环结束之后执行延迟回调
this.$nextTick(() => {
this._initBScroll() // 初始化better-scroll
})
}
},
在methods中编写_initBScroll()方法,实现better-scroll的初始化。
methods: {
……(原有代码)
// 初始化better-scroll
_initBScroll() {
this.leftBscroll = new BScroll('.menu-left', {
click: true,
mouseWheel: true
})
this.rightBscroll = new BScroll('.menu-right', {
click: true,
mouseWheel: true
})
}
}
上述代码中,使用new BScroll()创建一个实例,第1个参数表示对应的元素,第2个参数表示选项。在选项中,click表示是否允许单击,mouseWheel表示可以用鼠标滚动进行滚动。
6.一个页面很长可以用滚动条的值去控制
1-18day
1. 路由
固定路由缺点:路由规则的复用性差。动态路由可以传值
props传的是路由url的参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bI3qAskR-1645598896042)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220118151019115.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ZKkifxo-1645598896044)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220118151200685.png)]
router.beforeEach((to,form,next)=>{})
注意:
① 在守卫方法中如果不声明 next 形参,则默认允许用户访问每一个路由!
② 在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由!
2.导航栏
右边显示的这个位置,可以放组件,也可以一整个数据都放下去之后根据每个数据块的高度调整
3.细节点
<router-link :to="'/GoodList/' +menu.id" class="cate-item-wrapper"> </router-link>
在这里的 "’/GoodList/’+menu.id"是在拼接url的.
index.js
{
path: '/GoodList/:category_id',
component: Good,
props: true,
meta: { title: '商品列表' }
}
之后就接收到:category_id这里了,
props: ['category_id'],
个人经验认为:路由的不同是根据参数个数判断的,所以会把menu.id赋值给category_id然后传值给组价.组件的props名称与路由参数需要保持一致,不然不会识别
1-19day
1.数据代理
let number=185
let person = {
name:'lqc',
sex:'男',
}
Object.defineProperty(person,"height", {
// value:185,
// 可枚举的意思
// enumerable:true,
// // 可以被修改的意思
// writable:true,
// // 可以被删除的意思
// configurable:true,
get:function(){
return number
},
set(height){
number=height
}
})
data(){
return {
name:'java'
}
}
data里面的数据是通过defineProperty()数据代理上去的
let vm = new Vue({
el: "#one",
data: {
name:'java'
},
methods: {},
})
data是vm的一个属性,但是vm.data是找不到的.data会变成vm._data,所以vm._data就是data
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZhRZmsF-1645598896052)(D:\Desktop\image-20220119125642423.png)]
第一个:白箭头是到浏览器的运行过后的变化;两次都是数据代理,get方法可以让其他方法获取自己,set是用来修改自己的
小知识:
$开头的是给我们程序员准备的,_开头的是给vue底层使用的
2.深度监视
3.vue传参的方式
this.$http.get('goodslist', { params: { category_id: this.category_id } })
传参通过{ params : { category_id: this.category_id } }
Flex知识
Flex 布局语法教程
分类 编程技术
网页布局(layout)是CSS的一个重点应用。
布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。
2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。
Flex布局将成为未来布局的首选方案。本文介绍Flex布局的语法。
一、Flex布局是什么?
Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
任何一个容器都可以指定为Flex布局。
.box{
display: flex;
}
行内元素也可以使用Flex布局。
.box{
display: inline-flex;
}
Webkit内核的浏览器,必须加上-webkit前缀。
.box{
display: -webkit-flex; /* Safari */
display: flex;
}
注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。
二、基本概念
采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。
三、容器的属性
以下6个属性设置在容器上。
- flex-direction
- flex-wrap
- flex-flow
- justify-content
- align-items
- align-content
3.1 flex-direction属性
flex-direction属性决定主轴的方向(即项目的排列方向)。
.box {
flex-direction: row | row-reverse | column | column-reverse;
}
它可能有4个值。
- row(默认值):主轴为水平方向,起点在左端。
- row-reverse:主轴为水平方向,起点在右端。
- column:主轴为垂直方向,起点在上沿。
- column-reverse:主轴为垂直方向,起点在下沿。
3.2 flex-wrap属性
默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}
它可能取三个值。
(1)nowrap(默认):不换行。
(2)wrap:换行,第一行在上方。
(3)wrap-reverse:换行,第一行在下方。
3.3 flex-flow
flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
.box {
flex-flow: <flex-direction> <flex-wrap>;
}
3.4 justify-content属性
justify-content属性定义了项目在主轴上的对齐方式。
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。
- flex-start(默认值):左对齐
- flex-end:右对齐
- center: 居中
- space-between:两端对齐,项目之间的间隔都相等。
- space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
3.5 align-items属性
align-items属性定义项目在交叉轴上如何对齐。
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。
- flex-start:交叉轴的起点对齐。
- flex-end:交叉轴的终点对齐。
- center:交叉轴的中点对齐。
- baseline: 项目的第一行文字的基线对齐。
- stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
3.6 align-content属性
align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.box {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
该属性可能取6个值。
- flex-start:与交叉轴的起点对齐。
- flex-end:与交叉轴的终点对齐。
- center:与交叉轴的中点对齐。
- space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
- space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
- stretch(默认值):轴线占满整个交叉轴。
四、项目的属性
以下6个属性设置在项目上。
- order
- flex-grow
- flex-shrink
- flex-basis
- flex
- align-self
4.1 order属性
order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
.item {
order: <integer>;
}
4.2 flex-grow属性
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
.item {
flex-grow: <number>; /* default 0 */
}
如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
4.3 flex-shrink属性
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
.item {
flex-shrink: <number>; /* default 1 */
}
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
负值对该属性无效。
4.4 flex-basis属性
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
.item {
flex-basis: <length> | auto; /* default auto */
}
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
4.5 flex属性
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
.item {
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
4.6 align-self属性
align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
.item {
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
该属性可能取6个值,除了auto,其他都与align-items属性完全一致。
1-20day
1. 在vue中拼接url的方式有很多
比如例子一:
<router-link :to="{name:'GoodInfo',params:{id:item.id}}"></router-link>
这种方式必须要在路由文件之中设置name的值
{
path: '/GoodInfo/:id',
name: 'GoodInfo',
component: GoodInfo,
props: true,
meta: { title: '商品详情' }
}
例子二:
<router-link :to="'/GoodList/' +menu2.id" class="cate-item-wrapper"></router-link>
直接拼接
还有发送的请求:
this.$http.get('goodsinfo', { params: { id: this.id } })
商品的余量可以用来判断是否有货然后进行显示
-
@change当输入框失焦的时候触发而且在elementUI中使用change时是这样的@visible-change(类似于:等你输入完成之后在启动事件)
@input是输入框发生变化时触发,也就是说输入框一动就出触发了(一变化就启动事件)
2.进入购物车前需要商品的id,还有购买的数量
1-21day
some()
定义和用法
some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
some() 方法会依次执行数组的每个元素:
如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
注意: some() 不会对空数组进行检测。
注意: some() 不会改变原始数组。
数组添加
array.push() 从尾添加
array.unshift() 从头添加
:key diff算法
- 相当于身份证号,写了之后不会在真实的DOM浏览器出现的,是vue底层在使用.
- 虚拟dom在内存之中,key是索引的话,
不加key默认是加index
会用虚拟dom的key看看里面的东西有没有变化,没有变化就使用真实Dom,有变化的话,看里面的新的变为新的dom,旧的继续复用
1-22day
localstorange的key改变的方式,就是相当于给对应的key重新赋值,就会把原来的值覆盖掉
在实现购物车功能的时候,由于每次刷新页面后需要将购物车中的商品保留下来(vuex刷新之后就没有了),所以需要保存购物车中的商品信息到localStorage中
setItem()方法接受name和item形参,在setItem()方法中,通过localStorage本地存储对象的setItem()方法实现本地数据存储功能,其中参数name表示名称**,JSON.stringify(item)表示转换为JSON字符串后的值。**getItem()方法接受name形参,在getItem()方法中,返回一个经过JSON.parse()转换后的JSON对象,其中localStorage.getItem()方法表示获取指定名称的值。
1-23day
更新
v-model=“getGoodsSelected[item.id]”
:checked=“getGoodsSelected[item.id]”
vuex传送的数据的例子:
<template>
<div class="shopcar-container">
<div class="goods-list">
<div class="mui-card">
<div class="mui-card-content" v-for="item in shopgoods" :key="item.id">
<div class="mui-card-content-inner flex">
<!-- 复选框 -->
<div class="mui-input-row mui-checkbox mui-left">
<label> </label>
<input type="checkbox">
</div>
<!-- 中间商品图片 -->
<img :src="item.image">
<!-- 右侧部分 -->
<div class="info">
<h1>{{ item.name }}</h1>
<p class="flex">
<span class="price">¥{{ item.price }}</span>
<numbox :initcount="getGoodsCount[item.id]" :max="item.num" :goodid="item.id"></numbox>
<a href="#">删除</a>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import numbox from '../components/numbox.vue'
export default {
name: 'Shopcar',
components: {
numbox
},
data () {
return {
shopgoods: ''
}
},
computed: {
// 将他制作为computed的属性
// car为制作名称
...mapState('shopcar', ['car']),
...mapGetters('shopcar', ['getGoodsCount'])
},
created () {
this.getGoodList()
console.log('....')
console.log(this.getGoodsCount)
},
methods: {
getGoodList () {
console.log(this.car)
const arr = []
this.car.forEach(item => {
arr.push(item.id)
})
this.$http.get('shopcart', { params: { ids: arr } }).then(res => {
console.log(res.data)
this.shopgoods = res.data.data
})
}
}
}
</script>
<style lang="scss" scoped>
</style>
vue中把页面之间传输的数据,变为放在vuex之中,在页面之中可以利用…mapGetters(‘页面’,[‘方法’])把vuex中的State搬运过来成为计算属性,进行利用,对vuex的数据进行筛选的时候,会出现一整个vuex只需要一个数据的情况,这个时候就需要利用商品id进行查询了
修改vuex里面的数据,需要根据id先遍历,然后进行修改,然后提交到localrange()
input的监控有@change和@input
普通的有data加上watch
json特点
1-24day
:disabled是废除功能的意思
:disabled=“item.num===0”
vuex存的是各个页面之间传递的信息,比如上一个购物页面要存多少件物品,到下个页面展示,还有勾选数量,还有商品id,可以拿来给axios发送请求,获得更多信息,进行补充完整.
splice()用于添加或者删除,(第一个是索引,第二个是删除的个数,第三个参数是添加的元素)
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2,0,"Lemon","Kiwi");
-
created与beforeMount之间就是找到模板,然后转化为虚拟dom
-
beforeUpdate是数据是新的,页面是旧的;更新数据之后会更新虚拟Dom,进行对比之后更新真实Dom
-
1-25day
1.没有scoped的时候,看组件引入的顺序,决定css的值,因为没有scoped所有组件的css都是汇总到一起的,难免发生冲突;有scoped之后,在组件之内就这各css只能在组件内使用
less不可以下载,有时候需要考虑webpack的版本问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3CiWMSi-1645598896091)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220125124626671.png)]
数据代理
在这里,外面number改变了,里面person的number值不会发生改变,就比如vm._data的age值发生改变了,vm.age不会发生变化
用Object.defineProperty(对象,key,value)当外面的number发生变化了,调用person的age也会发生变化,这样就形成了关联
get的名字
vm.name用的是vm._data里面的name数据,修改vm.name也会使vm._data里面的数据发生变化
Observer数据监视
Vue.set()
用普通的办法,sex是会添加上去,但是没有get() set(),这样他就不是响应式的了,后添加的数据是不添加响应式的,也不在vm里面
vue.set()与vue.$set()是一样的
vm._data.student===vm.student是一样的
但是set()方法只能添加到data的对象(student)里面,是不可直接添加到data的
组件库选择
vue.use()详情
component.name是组件名字就是<名字>实例化的组件的名字,component是引入的组件
数组修改
数组的添加删除,pop() push() unshift(),shift(),splice(),sort(),reverse()
1-26day
js的阻塞特性?
v-clock特性
v-pre
不编译所在节点
v-once
- v-once所在节点初次渲染之后,就被视为静态内容了.2.以后数据更新不会引起v-once的里面数据的更新
自定义指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="one">
<h2 v-text="a"></h2>
<h3 v-big="20"></h3>
<input type="text" placeholder="123" autofocus>
<button @click="a++">+</button>
</div>
<script src="./vue.js"></script>
<script>
let vm = new Vue({
el: "#one",
data: {
a:1,
},
directives:{
big:function(element,binding){
// 指令就是放在dom节点里面后就是调用
console.log("big");
// 真实dom
console.log(element);
console.log(binding);
element.innerText=binding.value*10
},
focus(element,binding){
element.focus()
}
},
methods: {},
})
</script>
</body>
</html>
传统开发模式
缺点:
1. js文件互相调用,要理清楚js之间的依赖关系,不然直接报错,较为繁琐,还有css要关注到底那个文件使用了
2.css修改了,其他html样式就无法使用了.还有就是同名就会覆盖错误
3. 模块化知识
复用性差,复用起来麻烦
a.js中定义一个方法
function a(){
}
b.js中定义一个方法
function b(){
}
在a.js中访问b.js中方法
b();//就可以直接访问b方法
解决函数,命名冲突,出现了模块化
vue的scoped的优点就是很好的解决了冲突的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4POGUib-1645598896111)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220126150018512.png)]
把页面的一整个js拆分成很多份就是模块化
export 和export default 的区别在于:export 可以导出多个命名模块,例如:
//demo1.js
export const str = 'hello world'
export function f(a){
return a+1
}
对应的引入方式:
//demo2.js
import { str, f } from 'demo1'
export 导入的时候需要要记住变量名和函数名字,为了方便所以有了**,默认导出导入时候必须有花括号。**
export default 只能导出一个默认模块,这个模块可以匿名,例如:
//demo1.js
export default {
a: 'hello',
b: 'world'
}
对应的引入方式:
//demo2.js
import obj from 'demo1'
引入的时候可以给这个模块取任意名字,例如 “obj”,且不需要用大括号括起来。
js的模块化进程
现在前端技术日新月异,对于同一个问题痛点,各个时段有各自的解决方案,这就带来了很大差异。今天我就打算梳理js模块化的历史进程,讲一讲这些方案要做什么,怎么做。
js模块化进程的起因
现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。当一个项目开发的越来越复杂的时候,你会遇到一些问题:命名冲突(变量和函数命名可能相同),文件依赖(引入外部的文件数目、顺序问题)等。
JavaScript发展的越来越快,超过了它产生时候的自我定位。这时候js模块化就出现了。
webpack
1.先用fs模块读取模块内容,之后用bebal处理依赖关系
router-view
是切换的时候是会走一遍生命周期的,有
-
linkActiveClass: ‘mui-active’,linkActiveClass会在点击的router-link增加一个class属性
1-28day
eventLoop
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rq2UXCEz-1645598896119)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220128223949647.png)]
微任务和宏任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHhhSdpT-1645598896120)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220129154815026.png)]
localstorage
storage储存的意思
形成搜索记录
localstorage存的是Json数据
use()
1-30day
1.什么是跨域?
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
过程已经请求了,已经响应了,但是浏览器不提供数据给我们使用
jsonp 利用了同源策略不会限制script 不会限制src的特性
服务器与服务器之间传输数据是用http传输,浏览器和服务器是用ajax请求
代理服务器:
前端本地是8080端口,服务器的资源是5050/student
加了代理服务器之后,前端的请求是8080/student,代理服务器是5050
加了一个atguigu可以发送给多个服务器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qrmY57S-1645598896137)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20220201175940877.png)]
关闭eslint语法
vue3
0.拉开序幕的setup
-
理解:Vue3.0中一个新的配置项,值为一个函数。
-
setup是所有Composition API(组合API)“ 表演的舞台 ”。
-
组件中所用到的:数据、方法等等,均要配置在setup中。
-
setup函数的两种返回值:
-
若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
-
若返回一个渲染函数:则可以自定义渲染内容。(了解)
import {h} from 'vue' //返回一个渲染函数 return ()=>h('h1','硅谷')
-
-
注意点:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
- 如果有重名, setup优先。
- setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
- 尽量不要与Vue2.x配置混用
1.main.js
import { createApp } from 'vue'
// 2版本的是Vue构造函数,3是createdApp工厂函数,这是一个新的api
import App from './App.vue'
// import Vue from 'vue'
// mount挂载
// app会比vm更加轻量
// Vue构造函数在vue3中是没有定义的
const app=createApp(App)
app.mount('#app')
setTimeout(()=>{
app.unmount()
},1000)
new Vue({
render:(h)=> h(App)
}).$mount('#app')
可以不用根标签
2.watch
3.ref
// 情况一,监视ref的数据
watch(
name,
(newval, oldval) => {
console.log(newval, oldval)
},
{}
)
// 情况二:监视多个ref数据所定义的数据
watch(
[name, msg],
(newval, oldval) => {
console.log(newval, oldval)
},
{ immediate: true, deep: true }
)
4. reactive
监视一整个对象
// 三,监视reactive所定义的全部数据,oldval失效了,
// 可以把需要监视的数据另外拿出来放到ref之中,这样就有oldval功能了
// 默认开启了深度监视功能
watch(person, (newval, oldval) => {
console.log(newval, oldval)
}, {})
监视一阵个对象里面的属性
// 情况四:监听reactive的某个属性,可以替代情况三
watch(()=>person.name,(newval,oldval)=>{
console.log(newval,oldval);
},{})
监视一阵个对象里面的对象
watch(
() => person.job,
(newval, oldval) => {
console.log(newval, oldval)
},
{deep:true}
)
这里是reactive的某个对象(不是reactive),所以开启deep:true
对象类型获取不到oldval
5.computed
要有return返回值
let a=computed(()=>{
return person.name+"-"+person.job.a
})
6.watchEffect
从属性中拿值出来,然后变化属性,就会触发watchEffect函数
watchEffect(()=>{
let a1=name.value
console.log('触发了watchEffect',a1);
})
-
watch的套路是:既要指明监视的属性,也要指明监视的回调。
-
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
7.生命周期
vue2是先走beforecreate和create钩子函数,然后在寻找el或者$mount选项,而vue3一开始就要准备好createApp和mount函数,然后才走beforecreate和create钩子函数,相当于把mount提前到最开始
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名: beforeDestroy改名为 beforeUnmount
destroyed改名为
unmounted
Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
8.window添加删除事件
let page = reactive({
x: 0,
y: 0
})
function savepoint(event) {
page.x = event.pageX
page.y = event.pageY
console.log(event.pageX, event.pageY)
}
onMounted(() => {
window.addEventListener('click', savepoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savepoint)
})
removeEventListener(),第一个删除是什么事件,第二个是函数(这个函数需要独立出来)
9.窗口的坐标
1、pageX和pageY:
相对于浏览器中完全呈现内容区域的左上角。此参考点位于左上角的URL栏和后退按钮下方。这一点可以在浏览器窗口中的任何位置,
并且如果在页面中嵌入了嵌入的可滚动页面并且用户移动滚动条,则实际上可以改变位置。
2、screenX和screenY:
相对于物理屏幕/监视器的左上角,只有增加或减少监视器数量或监视器分辨率时,此参考点才会移动。
3、clientX和clientY:
相对于浏览器窗口的内容区域(视口)的左上边缘。即使用户从浏览器中移动滚动条,此点也不会移动。
10.hook
hook函数,是对setup()里面的功能进行封装的,
建立一个hook文件夹,然后下面一个js文件
import { reactive,onMounted,onBeforeUnmount } from "vue"
export default function(){
let page = reactive({
x: 0,
y: 0
})
function savepoint(event) {
page.x = event.pageX
page.y = event.pageY
console.log(event.pageX, event.pageY)
}
onMounted(() => {
window.addEventListener('click', savepoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savepoint)
})
return page
}
需要有return函数进行导出暴露,用函数包裹
import usepoint from '../hook/pomint'
export default {
setup() {
let show = ref(true)
let page=usepoint() //这里相当于启动hook函数并且进行保存数据
return { show ,page}
11. toRef和toRefs
这两个的作用就是方便使用,
toRef和Ref区别,就是toref都是使用person里面的,相当于桥接出来的数据
let person = reactive({
name: 'lqc',
age: '18',
job: {
j1: {
salary: 2000
}
}
})
console.log(person);
console.log(toRef(person, 'name'))
// console.log(person.name)
// console.log(ref(person.name))
return {
name:toRef(person,"name"),
age: toRef(person, 'age'),
person,
salary: toRef(person.job.j1, 'salary')
}
toRefs相当于多个toRef
下面相当于把person里面的属性,拆出来用属性return给页面
使用:
<div>
<div>{{name}}</div>
<div>{{age}}</div>
// 下面这里体现了toRefs的作用,只少了一个person层
<div>{{job.j1.salary}}</div>
<button @click="age++">addsalary</button>
<button @click="name+='1'">+</button>
</div>
return {
name: toRef(person, 'name'),
person,
age: toRef(person, 'age'),
salary: toRef(person.job.j1, 'salary'),
...toRefs(person)
}
shallowReactive监视第一层的
ref,shoallow的value值是Proxy的话就是响应式的,如果不是proxy的类型,是object那就不是响应式
12.shallowReactive 与 shallowRef
-
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
-
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
-
什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
13.readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。(可以使reactive和ref只读),readonly的参数是响应式的数据
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
let person = reactive({
name: 'lqc',
age: '18',
job: {
ppp:1,
j1: {
salary: 2000
}
}
})
person=readonly(person)
console.log(person);
readonly的person会把对象的person覆盖,导致无法修改数据
14.toRaw 与 markRaw
心得:普通数据到响应式数据的是ref,reactive.从响应式数据到普通数据是toRaw
- toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
15.provide 与 inject
心得:数据通信有props,emit,还有mibus传递方式,现在感觉这个可以更好的传值,可以替代minibus
-
作用:实现祖与后代组件间通信
-
套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据 -
具体写法:
-
祖组件中:
setup(){ ...... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ...... }
-
后代组件中:
setup(props,context){ ...... const car = inject('car') return {car} ...... }
-
16.响应式数据的判断
-
isRef: 检查一个值是否为一个 ref 对象
-
isReactive: 检查一个对象是否是由
reactive
创建的响应式代理 -
isReadonly: 检查一个对象是否是由
readonly
创建的只读代理 -
isProxy: 检查一个对象是否是由
reactive
或者readonly
方法创建的代理readonly返回的也是一个proxy对象
Composition API 的优势
1.Options API 存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
2.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起,要配合hook函数使用,才能发挥组合式api的作用,就是根据业务功能打包,不需要在data,methods,watch大量的数据之中寻找需要的方法,组合式api封装在一起就节约了寻找的时间
function就是一个hook函数
2.Teleport
-
什么是Teleport?——
Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。 -
原因在于如果我们嵌套在
Vue
的某个组件内部,那么处理嵌套组件的定位、z-index
和样式就会变得很困难(用于处理嵌套组件内部的复杂定位问题,)<teleport to="移动位置"> <div v-if="isShow" class="mask"> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div> </teleport>
3遮罩层
<template>
<teleport to="body">
<div class="mask" v-if="status">
<div class="diolang">
弹窗
<div>内容1</div>
<div>内容2</div>
<div>内容4</div>
</div>
</div>
</teleport>
<button @click="status=!status">change</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
let status = ref(false)
return {
status
}
}
}
</script>
<style scoped>
.mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: black;
opacity: 0.5;
}
.diolang {
position: absolute;
width: 100px;
background-color: bisque;
top: 45%;
left: 45%;
}
</style>
遮罩底层在遮住整个屏幕,被遮罩层
4.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>
default是出现后显示的,fallback是出现前显示的,使用异步组件的话,需要使用Suspense标签
异步组件
APP.vue
<template> <div class="father"> 父组件 <Suspense> <template v-slot:default> <children></children> </template> <template v-slot:fallback> <h2>加載中</h2> </template> </Suspense> </div> </template> <script> // import children from './components/children.vue' import { defineAsyncComponent } from 'vue' const children = defineAsyncComponent(() => import('./components/children.vue')) export default { components: { children } } </script> <style> .father { width: 200px; /* height:200px; */ background-color: orange; } </style>
Children.vue
<template> <div class="children"> 子组件 {{num}} </div> </template> <script> import Son from '../components/Son.vue' export default { name:"Chilren", components: { Son }, async setup(){ let num=10 let a=new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve({num}) },1000) }) return await a } } </script> <style scoped> .children { width: 150px; /* height: 170px; */ background-color: red; } </style>
await是相当于then拿到请求之后的结果
-
Typecript
-
const
是对let
的一个增强,它能阻止对一个变量再次赋值。 -
props: { todo: Object as ()=> Todo }, 函数返回的是Todo接口类型
-
const state = reactive<{todos:Todo[]}>({todos:[ {} ,{},{} ]})
-
interface Person1{ name: string, } let p1: Person1 = { name:'java' } let p2 = p1 p2.name=12 //这里会报错,因为p2和p1使用的是同一个堆内存中的数据,类型保护的是堆内存中的数据