如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
【Vue原理】Watch - 白话版
简述 响应式
监听的数据改变的时,watch 如何工作
设置 immediate 时,watch 如何工作
设置了 deep 时, watch 如何工作
举栗子
结论
没有设置 deep
设置了 deep
实际证明
专注 Vue 源码分享,为了方便大家理解,分为了白话版和 源码版,白话版可以轻松理解工作原理和设计思想,源码版可以更清楚内部操作和 Vue的美,喜欢我就关注我的公众号,公众号的文章,排版更好看
如果你觉得排版难看,请点击下面公众号链接
【Vue原理】Watch - 白话版
今天我们用白话文解读 watch 的工作原理,轻松快速理解 watch 内部工作原理。你说,你只懂怎么用的,却不懂他内部是怎么工作的,这样能有什么用?
近期有篇 《停止学习框架》很火,其实本意不是让我们不要学框架,而是让我们不要只停留在框架表面,我们要学会深入,以一敌十,让我们也要学会框架的抽象能力
watch 想必大家用得应该也挺多的,用得也很顺,如果你顺便花一点点时间了解一下内部的工作原理,相信肯定会对你的工作有事半功倍的效果
watch 的工作原理其实挺简单的,如果你有看过我之前讲解其他选项的文章,你可以一下子就明白 watch 是如何工作的,所以这边文章我也✍得很快
根据 watch 的 api,我们需要了解三个地方
1、监听的数据改变的时,watch 如何工作
2、设置 immediate 时,watch 如何工作
3、设置了 deep 时,watch 如何工作
简述 响应式
Vue 会把数据设置响应式,既是设置他的 get 和 set
当 数据被读取,get 被触发,然后收集到读取他的东西,保存到依赖收集器
当 数据被改变,set 被触发,然后通知曾经读取他的东西进行更新
如果你不了解,可以查看下 以前的文章
【Vue原理】响应式原理 - 白话版
监听的数据改变的时,watch 如何工作
watch 在一开始初始化的时候,会 读取 一遍 监听的数据的值,于是,此时 那个数据就收集到 watch 的 watcher 了
然后 你给 watch 设置的 handler ,watch 会放入 watcher 的更新函数中
当 数据改变时,通知 watch 的 watcher 进行更新,于是 你设置的 handler 就被调用了
设置 immediate 时,watch 如何工作
当你设置了 immediate 时,就不需要在 数据改变的时候 才会触发。
而是在 初始化 watch 时,在读取了 监听的数据的值 之后,便立即调用一遍你设置的监听回调,然后传入刚读取的值
设置了 deep 时, watch 如何工作
我们都知道 watch 有一个 deep 选项,是用来深度监听的。什么是深度监听呢?就是当你监听的属性的值是一个对象的时候,如果你没有设置深度监听,当对象内部变化时,你监听的回调是不会被触发的
在说明这个之前,请大家先了解一下
当你使用 Object.defineProperty 给 【值是对象的属性】 设置 set 和 get 的时候
1如果你直接改变或读取这个属性 ( 直接赋值 ),可以触发这个属性的设置的 set 和 get
2但是你改变或读取它内部的属性,get 和 set 不会被触发的
举栗子
var inner = { first:1111 }
var test={ name:inner }
Object.defineProperty(test,"name",{
get(){
console.log("name get被触发")
return inner
},
set(){
console.log("name set被触发")
}
})
// 访问 test.name 第一次,触发 name 的 get
Object.defineProperty(test.name,"first",{
get(){
return console.log("first get被触发")
},
set(){
console.log("first set被触发")
}
})
// 访问 test.name 第二次,触发 name 的 get
var a = test.name
// 独立访问 first 第一次
var b= a.first
// 独立访问 first 第二次
b= a.first
// 独立改变 first
a.first = 5
能看到除了有两次需要访问到 name,必不可少会触发到 name 的 get
之后,当我们独立访问 name 内部的 first 的时,只会触发 first 的 get 函数,而 name 设置的 get 并不会被触发
结论
看上面的例子后,所以当你的 data 属性值是对象,比如下面的 info
data(){
return {
info:{ name:1 }
}
}
此时,Vue在设置响应式数据的时候, 遇到值是对象的,会递归遍历,把对象内所有的属性都设置为响应式,就是每个属性都设置 get 和 set,于是每个属性都有自己的一个依赖收集器
首先,再次说明,watch初始化的时候,会先读取一遍监听数据的值
没有设置 deep
1、因为读取了监听的 data 的属性,watch 的 watcher 被收集在 这个属性的 收集器中
设置了 deep
1、因为读取了监听的data 的属性,watch 的 watcher 被收集在 这个属性的 收集器中
2、在读取 data 属性的时候,发现设置了 deep 而且值是一个对象,会递归遍历这个值,把内部所有属性逐个读取一遍,于是 属性和 它的对象值内每一个属性 都会收集到 watch 的 watcher
于是,无论对象嵌套多深的属性,只要改变了,会通知 相应的 watch 的 watcher 去更新,于是 你设置的 watch 回调就被触发了
实际证明
证明 watch 的 watcher 深度监听时是否被内部每个属性都收集
我在 Vue 内部给 watch 的 watcher 加了一个 属性,标识他是 watch 的 watcher,并且去掉了多余的属性,为了截图短一点
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
1.无序列表
无序列表是网页中最常用的列表,之所以称为“无序列表”,是因为其各个列表项之间为并列关系,没有顺序级别之分,列如:
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
</ul>
2.有序列表
有序列表即为有顺序的列表,其各个列表项会按照一定的顺序排列,列如:
<ol>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
</ol>
注:列表可以嵌套。
3.超链接标记< a>
1.超链接
一个网站通常由多个页面构成,进入网站时首先看到的是其首页,如果想从首页跳转到其子页面,就需要在首页相应的位置添加超链接。其基本语法格式为:
<a href="跳转目标" target=“目标窗口的弹出方式”>文本或者图像</a>
1
其中,target有两种取值方式:
–blank (在新窗口中打开)-self(默认在本窗口打开)
2.伪类
超链接标记< a >的伪类 含义
a:link{ CSS样式规则; } 未访问时超链接的状态
a:visited{ CSS样式规则;} 访问之后超链接的状态
a:hover{ CSS样式规则;} 鼠标经过,悬停时超链接的状态
a:active{ CSS样式规则;} 鼠标单击不动时超链接的状态
---------------------
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
HTML 中的div定义:
可定义文档中的分区或节(division/section)。
标签可以把文档分割为独立的、不同的部分。它可以用作严格的组织工具,并且不使用任何格式与其关联。
如果用 id 或 class 来标记 div,那么该标签的作用会变得更加有效。
用法
div是一个块级元素。这意味着它的内容自动地开始一个新行。实际上,换行是
固有的唯一格式表现。可以通过 div的 class 或 id 应用额外的样式。
不必为每一个 div都加上类或 id,虽然这样做也有一定的好处。
可以对同一个 div 元素应用 class 或 id 属性,但是更常见的情况是只应用其中一种。这两者的主要差异是,class 用于元素组(类似的元素,或者可以理解为某一类元素),而 id 用于标识单独的唯一的元素。
实例
<div id = " text"><div> <div class = " text1"><div> <div class = " text1"><div>
//div 中可以设置id属性,通过引用id属性来为div设置一些样式
//在style标签中,可以对你写的代码进行样式的设计,样式设计可以通过以下几种方法来写
1.通过引用id来设置样式,在id名称前加上# 格式: #id名称{ }
#text{ }
2.通过class来设置样式,class后面的名字可以是一样的,而id取名唯一,因此在需要设置同类型的样式时可以使用class来设置 格式: .class名称{ }
.text1{ }
3.通过标签名称来设置样式 格式: div{ }
div{ }
样式:
1.width :50px; // 宽度
2.height :50px;//高度
3.border : 1px solid red; //边框,border可以设置三个属性,分别是边框宽度,边框样式(实线,虚线等),边框颜色
4.margin:属性定义及使用说明
margin简写属性在一个声明中设置所有外边距属性。该属性可以有1到4个值。
实例:
margin:10px 5px 15px 20px;
上边距是 10px
右边距是 5px
下边距是 15px
左边距是 20px
margin:10px 5px 15px;
上边距是 10px
右边距和左边距是 5px
下边距是 15px
margin:10px 5px;
上边距和下边距是 10px
右边距和左边距是 5px
margin:10px;
所有四个边距都是 10px
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
1 状态共享
随着组件的细化,就会遇到多组件状态共享的情况,Vuex当然可以解决这类问题,不过就像Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,今天我们介绍的是vue.js 2.6新增加的Observable API ,通过使用这个api我们可以应对一些简单的跨组件数据状态共享的情况。
如下这个例子,我们将在组件外创建一个store,然后在App.vue组件里面使用store.js提供的store和mutation方法,同理其它组件也可以这样使用,从而实现多个组件共享数据状态。
首先创建一个store.js,包含一个store和一个mutations,分别用来指向数据和处理方法。
import Vue from "vue";
export const store = Vue.observable({ count: 0 });
export const mutations = {
setCount(count) {
store.count = count;
}
};
复制代码
然后在App.vue里面引入这个store.js,在组件里面使用引入的数据和方法
<template>
<div id="app">
<img width="25%" src="./assets/logo.png">
<p>count:{{count}}</p>
<button @click="setCount(count+1)">+1</button>
<button @click="setCount(count-1)">-1</button>
</div>
</template>
<script>
import { store, mutations } from "./store";
export default {
name: "App",
computed: {
count() {
return store.count;
}
},
methods: {
setCount: mutations.setCount
}
};
</script>
<style>
复制代码
你可以点击在线DEMO查看最终效果
2 长列表性能优化
我们应该都知道vue会通过object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要vue来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止vue劫持我们的数据呢?可以通过object.freeze方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
复制代码
另外需要说明的是,这里只是冻结了users的值,引用不会被冻结,当我们需要reactive数据的时候,我们可以重新给users赋值。
export default {
data: () => ({
users: []
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
},
methods:{
// 改变值不会触发视图响应
this.users[0] = newValue
// 改变引用依然会触发视图响应
this.users = newArray
}
};
复制代码
3 去除多余的样式
随着项目越来越大,书写的不注意,不自然的就会产生一些多余的css,小项目还好,一旦项目大了以后,多余的css会越来越多,导致包越来越大,从而影响项目运行性能,所以有必要在正式环境去除掉这些多余的css,这里推荐一个库purgecss,支持CLI、JavascriptApi、Webpack等多种方式使用,通过这个库,我们可以很容易的去除掉多余的css。
我做了一个测试,在线DEMO
<h1>Hello Vanilla!</h1>
<div>
We use Parcel to bundle this sandbox, you can find more info about Parcel
<a href="https://parceljs.org" target="_blank" rel="noopener noreferrer">here</a>.
</div>
复制代码
body {
font-family: sans-serif;
}
a {
color: red;
}
ul {
li {
list-style: none;
}
} import Purgecss from "purgecss";
const purgecss = new Purgecss({
content: ["**/*.html"],
css: ["**/*.css"]
});
const purgecssResult = purgecss.purge();
终产生的purgecssResult结果如下,可以看到多余的a和ul标签的样式都没了
4 作用域插槽
利用好作用域插槽可以做一些很有意思的事情,比如定义一个基础布局组件A,只负责布局,不管数据逻辑,然后另外定义一个组件B负责数据处理,布局组件A需要数据的时候就去B里面去取。假设,某一天我们的布局变了,我们只需要去修改组件A就行,而不用去修改组件B,从而就能充分复用组件B的数据处理逻辑,关于这块我之前写过一篇实际案例,可以点击这里查看。
这里涉及到的一个最重要的点就是父组件要去获取子组件里面的数据,之前是利用slot-scope,自vue 2.6.0起,提供了更好的支持 slot和 slot-scope 特性的 API 替代方案。
比如,我们定一个名为current-user的组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
复制代码
父组件引用current-user的组件,但想用名替代姓(老外名字第一个单词是名,后一个单词是姓):
<current-user>
{{ user.firstName }}
</current-user>
复制代码
这种方式不会生效,因为user对象是子组件的数据,在父组件里面我们获取不到,这个时候我们就可以通过v-slot 来实现。
首先在子组件里面,将user作为一个<slot>元素的特性绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
复制代码
之后,我们就可以在父组件引用的时候,给v-slot带一个值来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
复制代码
这种方式还有缩写语法,可以查看独占默认插槽的缩写语法,最终我们引用的方式如下:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
复制代码
相比之前slot-scope代码更清晰,更好理解。
5 属性事件传递
写过高阶组件的童鞋可能都会碰到过将加工过的属性向下传递的情况,如果碰到属性较多时,需要一个个去传递,非常不友好并且费时,有没有一次性传递的呢(比如react里面的{...this.props})?答案就是v-bind和v-on。
举个例子,假如有一个基础组件BaseList,只有基础的列表展示功能,现在我们想在这基础上增加排序功能,这个时候我们就可以创建一个高阶组件SortList。
<!-- SortList -->
<template>
<BaseList v-bind="$props" v-on="$listeners"> <!-- ... --> </BaseList>
</template>
<script>
import BaseList from "./BaseList";
// 包含了基础的属性定义
import BaseListMixin from "./BaseListMixin";
// 封装了排序的逻辑
import sort from "./sort.js";
export default {
props: BaseListMixin.props,
components: {
BaseList
}
};
</script>
复制代码
可以看到传递属性和事件的方便性,而不用一个个去传递
6 函数式组件
函数式组件,即无状态,无法实例化,内部没有任何生命周期处理方法,非常轻量,因而渲染性能高,特别适合用来只依赖外部数据传递而变化的组件。
写法如下:
在template标签里面标明functional
只接受props值
不需要script标签
<!-- App.vue -->
<template>
<div id="app">
<List
:items="['Wonderwoman', 'Ironman']"
:item-click="item => (clicked = item)"
/>
<p>Clicked hero: {{ clicked }}</p>
</div>
</template>
<script>
import List from "./List";
export default {
name: "App",
data: () => ({ clicked: "" }),
components: { List }
};
</script>
复制代码
<!-- List.vue 函数式组件 -->
<template functional>
<div>
<p v-for="item in props.items" @click="props.itemClick(item);">
{{ item }}
</p>
</div>
</template>
复制代码
7 监听组件的生命周期
比如有父组件Parent和子组件Child,如果父组件监听到子组件挂载mounted就做一些逻辑处理,常规的写法可能如下:
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
复制代码
这里提供一种特别简单的方式,子组件不需要任何处理,只需要在父组件引用的时候通过@hook来监听即可,代码重写如下:
<Child @hook:mounted="doSomething"/>
复制代码
当然这里不仅仅是可以监听mounted,其它的生命周期事件,例如:created,updated等都可以,是不是特别方便~
参考链接:
vueTips
vuePost
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="../static/vue.min.js"></script>
</head>
<body>
<div id="vue_data">
<h1>title : {{title}}</h1>
<h1>url : {{url}}</h1>
<h1>{{info()}}</h1>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#vue_data',
data: {
title: "Vue.js",
url: "https://cn.vuejs.org"
},
methods: {
info: function() {
return this.title + " - 坚持学习!";
}
}
})
</script>
</body>
</html>
1、每个Vue应用都需要实例化Vue来实现
var vm = new Vue({
//*******
})
2、el参数
在上面实例中的id为vue_data,在div元素中:
<div id="vue_data"></div>
意味着所有的改动均在这个id为vau_data的div中,外部不受影响。
3、定义数据对象
data用于定义属性,在上述实例中有2个属性,分别为:title、url。
methods用于定义函数,可以通过return来返回函数值。
{{ }}用于输出对象属性和函数返回值。
当一个Vue实例创建时,Vue的响应系统加入了data对象中能找到的所有属性。当这些属性的值发生改变时,html视图也会产生相应的变化。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="../static/vue.min.js"></script>
</head>
<body>
<div id="vue_data">
<h1>title : {{title}}</h1>
<h1>url : {{url}}</h1>
</div>
<script type="text/javascript">
//数据对象
var data = {title: "Vue.js",url: "https://cn.vuejs.org"}
var vm = new Vue({
el: '#vue_data',
data: data
})
//设置属性会影响到原始数据
vm.title = "spring";
document.write(data.title + "<br>");
//同样
data.url = "https://spring.io";
document.write(vm.url);
</script>
</body>
</html>
Vue还提供了实例属性与方法,以前缀$与用户定义的属性区分开来。
document.write(vm.$data === data) // true
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
使用新版本的弹性伸缩布局
display使用弹性伸缩盒
direction容器盒内元素的排列顺序
flex-wrap设置无法容纳时,自动换行
justify-content 伸缩项目的排列方式
align-items 处理额外空间
align-self 单独处理一个伸缩项目的额外空间
flex 控制伸缩容器的比例分配
order 设置伸缩项目出现的位置
dislpay
值:
flex 将容器盒作为块级弹性伸缩盒显示。
inline-flex:将容器盒作为内联级弹性伸缩盒显示。
实际现实中 这两个值没区别。
direction
容器盒内元素的排列顺序
值:
row:从左到右排列
row-reverse:从右到左排列
column:从上倒下排列
column-reverse从下到上排列
flex-wrap
设置无法容纳时,自动换行
值:
nowrap:不换行
wrap:自动换行
wrap-reverse:自动换行,方向和wrap相反
下图为正常排序
使用wrap-reverse后缩小浏览器时:
justify-content
伸缩项目的排列方式
值:
flex-start:伸缩项目以起始点靠齐
flex-end:伸缩项目以结尾靠齐
center:以中心点靠齐
space-between:伸缩项目平均分布
space-around:同上但两段保留一般的空间
实例:使用space-around的排列效果
align-items
处理额外空间
值:
flex-start:以顶部为基准,清理底部的额外空间
flex-end:以底部为基准,清理顶部的额外空间
center:以中间为基准,清理上下部分的额外空间
baseline:以基线为基准,清理额外的空间
stretch:伸缩项目填充整个容器,默认值
align-self
处理额外空间
值:与align-items的值一样,需要用nth-child()设置某一个需要处理的伸缩项目
flex
设置伸缩项目分配比例
p:nth-child(1)
{
flex: 1;
}
p:nth-child(2)
{
flex: 2;
}
p:nth-child(3)
{
flex: 2;
}
p:nth-child(4)
{
flex: 1;
}
order
设置伸缩项目出现的位置
p:nth-child(1)
{
order:2;
}
p:nth-child(2)
{
order:3;
}
p:nth-child(3)
{
order:4;
}
p:nth-child(4)
{
order:1;
}
---------------------
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
做了一个项目,有充值功能,充值方式为 微信和支付宝,效果如下:
代码:
<template>
<el-card class="box-card">
<ul class="msg-box">
<li>
<h4>我要充值</h4>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值金额</h4>
<el-radio-group v-model="amountVal" @change="amountChange">
<el-radio border :label="''+ 100">充值100</el-radio>
<el-radio border :label="''+ 500">充值500</el-radio>
<el-radio border :label="''+ 1000">充值1000</el-radio>
<el-radio border :label="''+ 2000">充值2000</el-radio>
<el-radio border :label="''+ 5000">充值5000</el-radio>
<el-radio border :label="''">自定义</el-radio>
</el-radio-group>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值方式</h4>
<el-radio-group v-model="rechargeParams.paymentType" @change="paymentTypeChange">
<el-radio border :label="''+ 0">微信</el-radio>
<el-radio border :label="''+ 1">支付宝</el-radio>
</el-radio-group>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值金额</h4>
<el-input :disabled="disabled" clearable v-model="rechargeParams.totalAmt" placeholder="请输入金额" style="width: 150px;"></el-input>
</li>
</ul>
<div style="text-align: center; margin-top: 30px;">
<el-button type="primary" @click="surePay">确认支付</el-button>
</div>
</el-card>
</template>
<script>
export default {
data() {
return {
amountVal: '',
disabled: false,
//充值参数
rechargeParams: {
"totalAmt": '', //金额
"paymentType": "0", //支付方式[0:微信,1:支付宝,2:余额,3:活动]
"transType": "0" //交易类型[0:充值,1:消费]
}
}
},
methods: {
//充值金额
amountChange(val) {
this.rechargeParams.totalAmt = val;
if (val == '') {
this.disabled = false
} else {
this.disabled = true
}
},
//支付方式
paymentTypeChange(val) {
this.rechargeParams.paymentType = val
},
//确认支付
async surePay() {
if (this.rechargeParams.totalAmt == '') {
this.$message.warning('请输入金额');
return;
}
const res = await this.$http.post('orderInfo/createOrderInfo', this.rechargeParams)
const {
code,
msg,
result
} = res.data
if (code === '200') {
//支付方式跳转
if (this.rechargeParams.paymentType == '0') {
this.$message.success('微信支付');
this.wechatPay(result);
} else if (this.rechargeParams.paymentType == '1') {
this.$message.success('支付宝支付')
const payDiv = document.getElementById('payDiv');
if (payDiv) {
document.body.removeChild(payDiv);
}
const div = document.createElement('div');
div.id = 'payDiv';
div.innerHTML = result;
document.body.appendChild(div);
document.getElementById('payDiv').getElementsByTagName('form')[0].submit();
} else if (this.rechargeParams.paymentType == '2') {
this.$message.success('余额支付成功');
this.$router.push({
name: 'order'
})
} else {
this.$message.success('活动支付')
}
} else if (code === 401) {
this.$message.error(msg)
this.$router.push({
name: 'login'
})
} else {
this.$message.error(msg)
}
},
//微信支付
wechatPay(result) {
if (result) {
const orderParams = JSON.parse(result);
sessionStorage.qrurl = orderParams.qrurl;
sessionStorage.amt = orderParams.amt;
sessionStorage.returnUrl = orderParams.returnUrl;
sessionStorage.order_id = orderParams.order_id;
this.$router.push({
name: 'wechatPay'
})
}
}
}
}
</script>
<style scoped>
/* 信息列表样式 */
.msg-box > li {
list-style: none;
border-bottom: 1px solid #c5c5c5;
padding: 20px 10px;
}
</style>
支付宝方式:后台会返回来一个form,然后提交form自动跳转到支付宝支付页面。
微信方式:需要自己根据后台返回的url生成二维码页面,如图所示:
代码:
<template>
<div class="payBox">
<div class="img-logo">
<img src="http://img.huoxingbeidiao.com/public/WePayLogo.png" alt="">
</div>
<div class="info-box">
<div style="padding-bottom: 20px;">
<qrcode-vue :value="qrurl" :size="200" level="H"></qrcode-vue>
</div>
<img src="http://img.huoxingbeidiao.com/public/WePayInfo.png" alt="">
<p class="price">¥ {{amt}}</p>
</div>
</div>
</template>
<script>
import QrcodeVue from 'qrcode.vue'
export default {
data() {
return {
amt: 0,
qrurl: '',
timer: null
}
},
components: {
QrcodeVue
},
methods: {
getOrderInfo() {
if (sessionStorage.qrurl && sessionStorage.amt) {
this.qrurl = sessionStorage.qrurl;
this.amt = sessionStorage.amt;
}
},
startLoop() {
this.timer = setInterval(() => {
this.isPaySuccess()
}, 3000)
},
async isPaySuccess() {
const orderId = sessionStorage.order_id;
const res = await this.$http.get('orderInfo/queryOrder?orderId=' + orderId)
const {
code,
msg,
resultList
} = res.data
if (code === '200') {
clearInterval(this.timer);
this.timer = null;
sessionStorage.removeItem('qrurl');
sessionStorage.removeItem('amt');
sessionStorage.removeItem('order_id');
sessionStorage.removeItem('returnUrl');
setTimeout(() => {
this.$router.push({
name: 'order'
})
}, 3000)
} else if (code === 401) {
clearInterval(this.timer);
this.timer = null;
sessionStorage.removeItem('qrurl');
sessionStorage.removeItem('amt');
sessionStorage.removeItem('order_id');
sessionStorage.removeItem('returnUrl');
this.$message.error(msg)
this.$router.push({
name: 'login'
})
} else {
}
}
},
created() {
this.getOrderInfo()
this.startLoop()
},
beforeDestroy() {
clearInterval(this.timer)
this.timer = null
}
}
</script>
<style scoped>
.payBox {
width: 1000px;
margin: 0 auto;
}
.payBox .img-logo {
padding: 20px 0;
text-align: center;
}
.payBox .img-logo img {
width: 180px;
}
.info-box {
padding: 60px 0;
border-top: 3px solid #F43B66;
-webkit-box-shadow: 0 0 32px 0 rgba(0, 0, 0, .18);
box-shadow: 0 0 32px 0 rgba(0, 0, 0, .18);
text-align: center;
}
.info-box .price {
color: #F43B66;
font-size: 40px;
padding-top: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #f1f1f1;
}
</style>
需要安装qrcode.vue
npm install --save qrcode.vue 或 yarn add qrcode.vue
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
ID选择器>类选择器>标签选择器
行内样式>内部样式>外部样式
*结构伪类选择器 一旦设置 不管在行内还是在后面重新设置,都没办法改变
*结构伪类选择器设置奇偶行以及从第三行开始变色如何实现
nth-cild(2N+3)表示从第三行开始的奇数行
同理 nth-child(2N+4)表示从第四行开始的偶数行
nth-cild(2N+5)表示从第五行开始的奇数行
*设置前三个:
p:nth-child(-n+3){
background-color: #b3d4fc;
}
*使用E F:nth-child(n)和E F:nth-of-type(n)的 关键点
E F:nth-child(n)在父级里从一个元素开始查找,不分类型
E F:nth-of-type(n)在父级里先看类型,再看位置
注意
child 跟子选择器没有关系,可以是子选择,也可以是后代选择 由层次选择器 (如table tr)来控制
1.层次选择器
table td 后代选择器 td包含在table里
div>p子选择器 p是div的子元素
p1+p p1后面一个兄弟p变化 是对p进行处理 p1不变 而且只是下面相邻的变化 上面相邻不变化
p1~p p1后面所有兄弟p变化 p1不变
2.结构伪类选择器
P:first-child 作为父元素的第一个子元素得元素 p
P:last-child 作为父元素的最后一个子元素得元素 p
P a :nth-child(n) p中第n个a元素 (even)(odd)
p:first-of-type 必须是p元素 不是子元素也行
p a:nth-of-type(n)
必须是a元素 不是a的子元素也行
3.属性选择器
a[id] a标签中含有id的
a[id=111] a标签中的id=111的
a[href*=http] a标签中包含href属性 且都包含http
a[href&=png] a标签中包含href属性 且最后以png结尾
a[href^=http://] a标签中包含href属性且以http://开头
1.层次选择器
*相邻兄弟选择器,只对后面的兄弟有作用,对前面的兄弟没效果。
*相邻兄弟选择器,E+F E不会产生效果效果
2.结构伪类选择器
使用E F:nth-child(n)和E F:nth-of-type(n)的 关键点
E F:nth-child(n)在父级里从一个元素开始查找,不分类型
E F:nth-of-type(n)在父级里先看类型,再看位置
注意
child 跟子选择器没有关系,可以是子选择,也可以是后代选择 由层次选择器 (如table tr)来控制
设置前三个:
p:nth-child(-n+3){
background-color: #b3d4fc;
}
3.属性选择器
举例:
a[href=^http]{backgroud-color:red}
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
微信小程序动画之点击效果
代码:
js:
// pages/test/test.js
Page({
containerTap: function (res) {
var that = this
var x = res.touches[0].pageX;
var y = res.touches[0].pageY + 85;
this.setData({
rippleStyle: ''
});
setTimeout(function () {
that.setData({
rippleStyle: 'top:' + y + 'px;left:' + x + 'px;-webkit-animation: ripple 0.4s linear;animation:ripple 0.4s linear;'
});
}, 200)
},
})
wxml:
<view class="ripple" style="{{rippleStyle}}"></view>
<view class="container" bindtouchstart="containerTap"></view>
wxss:
page{height:100%}
.container{
width:100%;
height:100%;
overflow: hidden
}
.ripple {
background-color:aquamarine;
border-radius: 100%;
height:10px;
width:10px;
margin-top: -90px;
position: absolute;
overflow: hidden
}
@-webkit-keyframes ripple {
100% {
webkit-transform: scale(12);
transform: scale(12);
background-color: transparent;
}
}
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 、平面设计服务。
如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里
这周萌芽决定好好学习一下ES6,感兴趣的小伙伴们来一起学习吧~
ES6(ES2015)——IE10+、Chrome、FireFox、移动端、Node.js
编译转换
1.在线转换(browser.js)
2.提前编译
ES6新特性
1.变量
2.函数
3.数组
4.字符串
5.面向对象
6.promise(串行化异步交互)
7.generator(把同步拆分为异步)
8.模块化(ES6自带模块化)
变量
var
1.可以重复声明
2.无法限制修改
3.没有块级作用域(没有语法块!)
let 不能重复声明(变量,可以修改)
const 不能重复声明(常量,不能修改)
块级作用域,let在外部无法调用
函数
箭头函数 =>
function show(){
//这是我们平常的函数
}
let show=()=>{
//箭头函数
}
//区别不大,把function省略掉换成箭头,主要就是为了方便,可传参
1.如果只有一个参数,()可以省去。
let show=a=>{
return a*2
}
2.如果只有一个return,{}可以省略
let show=a=>a*2;
let arr = [15,2,37,11,67,4,6]; //排序
arr.sort((n1,n2)=>{
return n1-n2;
})
arr.sort((n1,n2)=> n1-n2 );
console.log(arr)
函数的参数
1.参数扩展/展开
2.默认参数
参数的扩展
1.收集参数
function arrData(a,b,...args) {
alert(a);
alert(b);
alert(args);
}
*注意:Rest Parameter必须是最后一个(除其他语言)
1
2.展开数组
arrData(...arr); //等价于 arrData(1,2,3);
1
这仨点儿【…】代表把数组内容掏出来放这。
默认参数
//jQuery中的默认参数
$('#div1').animate({width:'200px'});
$('#div1').animate({width:'200px'},1000);
//ES6默认传参
function showOne(a,b=10,c=5) {
console.log(a,b,c)
}
解构赋值
1.左右两边解构必须一样
2.右边必须是个合法的东西
3.声明和赋值不能分开(必须在一句话里完成)
let [one,two,three] = [10,20,30];
let {one1,two2,three3} = {a:10,b:20,c:30};
数组
map 映射(一个对一个。传几个返回几个)
let result = arr.map(function (item) {
return item*2;
});//简写一下
let result = arr.map(item=>item*2 );
//判断考试成绩
let score = [19,18,78,65,100];
let result = score.map(item=>item>=60?'及格':'不及格');
reduce 汇总(算个总数,算个平均数)
//tmp:上次求和总和,为两两相加,如果之前没有结果则为传进来的数组的第一个数。
//itme:当前的数。
//index:执行的次数。
let result = arr.reduce(function (tmp, item, index) {
return tmp + item;
});
//简写
arr.reduce((tmp, item, index)=>tmp + item);
filter 过滤器(筛选掉不需要的)
let result = arr.filter(item=>{
if (item%2 == 0){
return true;
} else {
return false;
}
});
//简写
let result = arr.filter(item=>item%2 == 0);
//***萌芽在这里提一下!json和之前的item都不是固定的,可以随便命名。意思都是当前的值!
let arrProce = [
{title:'男士衬衫',price:75},
{title:'女士包包',price:5000},
{title:'男士包包',price:20},
{title:'女士鞋',price:2500}
];
let result = arrProce.filter(jsom=>json.price >= 2000);
console.log(result);
forEach循环(迭代)
arr.forEach((item,index)=>{
alert(index+":"+item)
})
字符串
1.多了俩新方法
startsWith(); //判断开头,返回布尔类型
endWith(); //判断结尾,返回布尔类型
let str='hello,world!'
str.startsWith('h')
str.endWith('!') //返回true
2.字符串模板
字符串连接
2.1直接把东西塞进字符串里面 ${东西}
2.2可以折行
<h1>${title}</h1>
<p>${content}</p>
ES6的面向对象
1.class关键字,构造器和类分开啦。
2.class里面直接加方法。
class User{
constructor(name,password){ //构造器
this.name = name;
this.password = password;
}
showName(){
alert(this.name);
}
showPass(){
alert(this.password);
}
}
var user = new User('萌芽子','123456');
user.showName();
user.showPass();
继承
class VipUser extends User{
constructor(name,password,age){
super(name,password); //super 超类
this.age = age;
}
showAge(){
alert(this.age)
}
}
var user = new VipUser('萌芽子','123456','18岁');
user.showName();
user.showPass();
user.showAge();
不得不说作为一只JAVA汪,这种写法真得劲!
面向对象的应用
React
1.组件化(class)
2.JSX(JSXbabelbrowser.js)
JSX属于JS的扩展版
class Test extends React.Component{
constructor(...args){
super(...args);
}
render(){
return <li>{this.props.str}</li> //props:属性
}
}
window.onload = function(){
let oDiv = document.getElementById('div1');
ReactDOM.render(
<ul>
<Item str="你好"></Item>
<Item str="世界!"></Item>
</ul>
oDiv
);
};
打卡,下次就学这个了!===============
json
1.JSON对象
JSON.stringify() json转字符串
let json = {a:10, b:20};//JSON.stringify 字符串化
let str = 'http://www.baidu.com/path/user?data='+JSON.stringify(json);
str = 'http://www.baidu.com/path/user?data='+encodeURIComponent(JSON.stringify(json));
alert(str)
1
2
3
4
JSON.parse() 字符串转json
let str = '{"a":12,"b":20,"c":"可乐"}';
let json = JSON.parse(str);
console.log(json);
2.简写
在新版的ES6当中名字一样的键(key)和值(value)可以只写一个。
let a = 12;
let b = 5;
let json = {a,b,c:21};
简化了JSON中的方法。
let json ={
a:12,
showJson(){
alert(this.a);
}
};
json.showJson();
json的标准写法:
1.只能用双引号
2.所有的名字都必须用引号包起来(所有的key都必须是双引号)
{a:12,b:5} × 错误的写法
{"a":"萌萌萌","b":"芽子"} √ 正确的写法
1
2
Promise(承诺)
异步:操作之间没啥关系,同时进行多个操作
同步:同时只能做一件事
优缺点:
异步:代码更复杂
同步:代码简单
Promise——消除异步操作
*用同步一样的方式来书写异步代码;
let p = new Promise(function (resolve,reject) {
//异步代码
//resolve——成功
//reject——失败
})
-----------------------------------------访问我们的arr.txt文件,这里用到了jQuery的ajax就不详细介绍了。
let p = new Promise(function (resolve, reject) {
//异步代码
//resolve——成功
//reject——失败
$.ajax({
url: 'arr.txt',
dataType: 'json',
success(arr) {
resolve(arr);
}, error(err) {
reject(err);
}
})
});
//结果
p.then(function (arr) {
alert('成功啦' + arr)
}, function (err) {
alert('失败了' + err)
console.log(err)
});
-----------------------------------------------多个请求地址
Promise.all([p1,p2]).then(function (arr){
let [res1,res2] = arr;
alert('全部成功啦');
console.log(res1);
console.log(res2);
},function (){
alert('至少有一个失败了');
});
----------------------------再简化
function createPromise(url){
return new Promise(function (resolve, reject) {
$.ajax({
url,
dataType: 'json',
success(arr) {
resolve(arr);
}, error(err) {
reject(err);
}
})
});
}
Promise.all([
createPromise('arr.txt'),
createPromise('json.txt')
]).then(function (arr){
let [res1,res2] = arr;
alert('全部成功啦');
console.log(res1);
console.log(res2);
},function (){
alert('至少有一个失败了');
});
----------------------完美写法
Promise.all([
$.ajax({url:'arr.txt',dataType:'json'}),
$.ajax({url:'json.txt',dataType:'json'})
]).then(function (results) {
let [arr,json] = results;
alert("成功了");
console.log(arr,json)
},function () {
alert("失败了")
})
我们有了promise之后的异步:
Promise.all([ $.ajax(),$.ajax() ]).then( results=>{
//对了
},err=> {
//错了
})
Promise.all (必须全部成功)
Promise.race(同事读多个数据,即使失败也没关系)
generator(生成器)
普通函数 - 一路到底执行不可中断
generator函数 - 可中断
function * show() {
alert('a');
yield;//暂时放弃执行
alert('b');
}
let genObj = show();
genObj.next();
genObj.next();
yield
yield传参
function * show(num1,num2) {
alert(`${num1},${num2}`);//es6
alert('a');
let a = yield;//暂时放弃执行
console.log(a);
alert('b');
}
let genObj = show(99,88);
genObj.next(12);//第一个next无法给yield传参的,废的
genObj.next(5);
yield返回
function *show() {
alert('a');
yield 12;
alert('b');
return 55;
}
let gen = show();
let res1 = gen.next();
console.log(res1); //{value: 12, done: false}
let res2 = gen.next();
console.log(res2);//{value: undefined, done: true} 加了return {value: 55, done: true}
还没做的菜叫函数参数,过程是yield之前函数里面的东西,干净的菜,切好的菜是中间过程也就是yield,最终我们将它返回出去!不得不说这图很生动。
异步操作
1.回调
$.ajax({
url:'url',
dataType:'json',
success(data){
$.ajax({
url:'xxx',
dataType: 'json',
success(data) {
//完事儿了
},error(err) {
alert('错了')
}
})
},error(){
alert('失败')
}
})
2.Promise
Promise.all([
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'})
]).then(results=>{
//完事儿
},err=>{
//错误的
})
3.generator
runner(function *(){
let data1 = yield $.ajax({ulr:xxx,dataType:'json'});
let data2 = yield $.ajax({ulr:xxx,dataType:'json'});
let data3 = yield $.ajax({ulr:xxx,dataType:'json'});
})
1
2
3
4
5
generator(不能用=>函数)
逻辑判断下非常好用。
Promise:一次读一堆。
generator:逻辑性。
runner(function *(){
let userData = yield $.ajax({url:'getUserData',dataType:'json'});
if(userData.type == 'VIP'){
let items = yield $.ajax({url:'getVIPItems',dataTyoe:'jsom'});
}else{
let items = yield $.ajax({url:'getItems',dataTyoe:'jsom'});
}
//生成...
}
})
总结
1.变量:
var:能重复声明、函数级
let: 严格的,不能重复声明,块级,变量
const:严格的,不能重复声明,块级,常量
2.箭头函数
2.1方便
i.如果只有一个参数,()可以省
ii.如果只有一个return,{}可以省
2.2修正了this
this相对正常点
3.参数扩展
…能收集
…能扩展
默认参数
4.数组方法
map 映射
reduce 汇总
filter 过滤
forEach 循环
5.字符串
starsWith/endWith
字符串模板:${a}xxx{b}
6.Promise
封装异步操作
Promise.all([]);
7.generator
function *show(){
yield
}
8.JSON
JSON.stringify({ a :12,b :5}) => {“a”:12,“b”:5}
JSON.parse(’{“a”:12,“b”:5}’) =>{a:12,b:5}//字符串
9.解构赋值
let [a,b,c] = [12,5,8];
左右结构一样,右边是个合法的东西,连生命带赋值一次完成。
10.面向对象
class Test(){
constructor(xxx){
this = xxx
}
方法1(){
}
方法2(){
}
}
继承
class Test2 extends Test(){
constructor(){
super();
}
}
谈谈ES7和ES8
1.数组includes
数组是否包含某个东西
2.数组 keys/values/entries
for…in(循环数组)
对于数组来讲循环的是下标
对于json循环的是key
for…of(循环迭代器)
对于数组循环的是值
不能用于JSON,json并不是迭代器
keys = >所有的key拿出来 0,1,2,3…
values =>所有的values拿出来 23,5,8,1…
entries =>所有的键值对拿出来 {key:0,value:a}
let arr = [12,5,8,99];
for(let [key,value] of arr.entries()){
alert(`${key} = ${value}`);
预览版,目前极大多数浏览器都不支持,以后可能会支持,了解一下就好。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 、平面设计服务。
蓝蓝 http://www.lanlanwork.com