笔记
一,
要使用一个组件里面的方法
<old-consume ref="oldConsume"></old-consume>
this.$refs.oldConsume.方法名()
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
为了让子节点不超出容器圆角边框,容器的overflow属性必须是除visible之外的其他值(比如 auto, hidden, scroll…)
二,
1、let
let声明的变量只在它所在的代码块内有效。
for循环的计数器,就很合适使用let命令。
for (let i = 0; i < 10; i++) {
console.log(i) //0,1,2,3,4,5,6,7,8,9
}
没有变量提升
let不允许在相同作用域内,重复声明同一个变量。
2、const
对于const来说,只声明不赋值,就会报错。
3,条件语句的优化
根据颜色找出对应的水果
const fruitColor = new Map()
.set('red', ['apple','strawberry'])
.set('yellow', ['banana','pineapple'])
.set('purple', ['grape','plum']);
functiontest(color) {
return fruitColor.get(color) || [];
}
test('red') // ['apple','strawberry']
4, finally—finally 语句在 then 和 catch 之后无论有无异常都会执行。
fetch('file.json')
.then(data => data.json())
.catch(error => console.error(error))
.finally(() => console.log('finished'));
5, 变量重命名
let { foo : baz } = { foo : 'aaa', bar : 'bbb'};
console.log( baz ); //"aaa"
6,Class:语法糖
class Point { //定义一个Point类
constructor ( x, y) { //构造方法
this.x=x;
}
}
三,微信小程序 时间戳转年月日时分秒
1、首先在utils下的util.js里面写转换逻辑
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
//时间戳换算年月日时分秒
var formatTime = function (number, format) {
var formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
var returnArr = [];
var date = new Date(number);
returnArr.push(date.getFullYear());
returnArr.push(formatNumber(date.getMonth() + 1));
returnArr.push(formatNumber(date.getDate()));
returnArr.push(formatNumber(date.getHours()));
returnArr.push(formatNumber(date.getMinutes()));
returnArr.push(formatNumber(date.getSeconds()));
for (var i in returnArr) {
format = format.replace(formateArr[i], returnArr[i]);
}
return format;
}
module.exports = {
formatTime: formatTime
}
2、在需要使用的页面的js文件中写下面代码
//首先引入util.js,使用绝对路径
var util = require('../../utils/util');
//需要使用的地方
var stamp = '0'
var userTime = util.formatTime(stamp, 'Y-M-D h:m:s')
console.log(userTime)
//这样就拿到了 年月日时分秒2000-01-01 00:00:00
如果只要年月日,只需要将var userTime = util.formatTime(stamp, 'Y-M-D h:m:s')改为var userTime = util.formatTime(stamp, 'Y-M-D')即可
四,微信小程序商城详情页使用富文本,即后台穿过来的数据是富文本的时候
1、wxml页面使用rich-text进行解析
<view class="details">
<rich-text nodes="{{text}}"></rich-text>
</view>
2、需要调整图片的大小、页面样式等,在页面对应的js中
按需只改变图片大小:text: res.data.data.details.replace(/\<img/g, '< img style="width:100%;height:auto;display:block;"')
全部中摘取需要改变的标签:html: fistD.content.replace(/\<img/g, '< img style="width:100%;height:auto;display:block"')
.replace(/\<p>/ig, '<p class="wx_add_p" style="font-size:12px">')
.replace(/\<span>/ig, '<sapn class="wx_add_p">')
.replace(/\<span/g, '<span style="font-size:17rpx"')
.replace(/\<div/g, '<div style="font-size:17rpx"'),
3、还可在wxss中改变样式,给rich-text标签加view
.details{
padding-bottom:80rpx;
}
五,微信小程序使用原生+第三方vant-weapp进行开发,首先将请求封装成方法,每次请求调用方法
1、首先在utils下新建request.js写封装方法
const baseUrl = "https://store.jiananyan.com";
function api(method, params,contentType) {
wx.showLoading({
title: '加载中...',
mask: true,
})
return new Promise(function (resolve, reject) {
wx.request({
url: baseUrl,
method: method?method:'post',
data: params,
header: {
"token": wx.getStorageSync('token'),
'content-type': contentType ? contentType : 'application/x-www-form-urlencoded' // 默认值
},
success: function (ret) {
wx.hideLoading();
if(ret.statusCode==200){
resolve(ret);
}else{
wx.showToast({
title: '请求失败,请稍候再试。',
icon:'none',
// image:'/image/noNetwork.png',
mask:true,
duration:2000,
})
}
},
fail: function (err) {
wx.hideLoading()
wx.showToast({
title: '请求失败,请稍候再试。',
icon:'none',
// image:'/image/noNetwork.png',
mask:true,
duration:2000,
})
setTimeout(function(){
wx.hideToast()
},2000)
reject(err);
}
})
})
}
export default api;
2、在需要的页面请求后台接口的时候写,先引入import api from '../../utils/request.js,注意使用绝对路径
然后请求后台接口
var params={appFun:'',id:'',goods:''},//后台j接口路径和需要的参数
api('post',params,'').then(res=>{}).catch(err=>{}) //第一个参数是请求方式。post,get,第二个参数是后台需要的参数,第三个参数是请求头,conten-type
六,js数组深拷贝
扩展运算符...
var arr1=[1,2,3]
var[...arr2] = arr1;
console.log(arr2) //[1,2,3]
七,微信小程序 图片自适应 不变形
将图片放进div里面,div给固定宽高,图片宽高100%,然后在给图片加上 mode="aspectFill" 标签 就能自动自适应
<view style="width:100rpx;height:100rpx;">
<image src="" mode="aspectFill" style="style:100%;height:100%;"></image>
</view>
八,div背景透明,不影响文字
background:rgba(255,255,255,0.5)
设置内外阴影:box-shadow:0px 0px 20px red inset,0px 0px 20px blue;
内阴影设置为红色,外阴影设置为蓝色,内外阴影的样式以逗号隔开
九,vue项目配置@为src
vue.config.js
const path = require('path')
const resolve = (file) => path.resolve(__dirname, file)
module.exports = {
chainWebpack: config => {
// 设置目录别名alias
config.resolve.alias
.set('@', resolve('src'))
},
};
安装sass
npm config set sass_binary_sitehttps://npm.taobao.org/mirrors/node-sass/
父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息
store vuex:
仓库:Vuex 和单纯的全局对象:1、vuex是响应式,2、改变 store 中的状态:提交 (commit) mutation
通过 store.state 来获取状态对象,通过 store.commit 方法触发状态变更
ction 在组件中被触发,分发action
action 接收一个commit 或者 state
不使用辅助函数mapActions :this.$store.dispath('sortNumStatus',arr);
十,vue导航守卫 = 路由守卫 = 进,出
携带数据进,事情完成才能出
一,全局导航守卫(写在main.js中,在进入路由前执行)
全局前置守卫
router.beforeEach(fn)
1,fn ==> 三个参数
to:要去哪里
from:从哪里来
next:通不通过
next() == next(true) : 从当前路由跳转到目标路由
next(false) : 不通过===> 从当前路由跳转不到目标路由
next( '/login' ) === next({path:'/login'}) 跳转指定的路由
next({path:'/login',params,query})
Eg:router.beforeEach( (to,from,next) => {
const name = localStorage.getItem('name'); //查看本地存储上是否有name对象
if( name || to.path === '/login'){ //短路逻辑,有就可以继续执行,没有就跳转到登陆页面
next()
} else {
next('/login') //跳转登陆页面
}
} )
二,路由独享守卫(写在路由表中的守卫钩子,针对和当前路由相关)
{
path: '/user',
component: User,
beforeEnter: (to,from,next) => {
const name = localStorage.getItem('name');
if (name) {
next()
} else {
setTimeout( () => {
alert('你还没有登录,点击跳转登录')
next('/login')
},0 )
}
}
}
三,组件内守卫(三种)
1,组件内的前置守卫:beforeRouteEnter( (to,from,next) => {})进入组件时触发
导航进入组件时,调用
this是访问不到的,如果非要访问this,必须通过 next( vm => {})访问
Eg:数据预载 == 进入组件前就获得数据
next ( vm=>{ //vm指组件
const result = JSON.parse (res.data.slice(7,-1)).rp_result.categorys
vm.$set (vm.category, 'categorys',result)
} )
2,组件内的后置守卫:离开组件时,调用,this是可以访问到
//判断两个输入框是否有值输入,有就可以直接离开,没有弹窗告诉确定是否离开,防止误触
beforeRouteLeave (to,from,next) {
if (this.username && this.pwd) {
next()
} else {
if (confirm ('你确定要离开吗?')) { //返回一个布尔值
next()
} else {
next(false)
}
}
}
3,组件内的更新守卫(路由传参+路由接参)
在当前路由改变,但是该组件被复用时调用
Eg:对于一个带有动态参数的路径 /foo/:id,在/foo/1 和/foo/2 之间跳转的时候
由于会渲染同样的Foo组件,因此组件实例会被复用,这个钩子就会在此情况下被调用
可以访问组件实例this
{
path : '/movies',
component: Movies ,
children : [ //写一个二级路由
{
path : 'hot/:id' ,
component : Hot ,
name : 'hot'
}
]
}
Hot.vue
<template>
<div>
<p> id : {{this.$route.params.id}} </p>
<p> query : {{this.$route.query.text}}</p>
</div>
</template>
<script>
export default {
//二级路由组件,当复用一个组件并且数据改变时触发这个钩子函数
beforeRouteUpdate ( to, from, next ) {
console.log( 'update' )
next()
}
}
</script>
Movies.vue
<template>
<div>
//传参
<router-link :to = "{name : 'hot' , params : {id :1 } , query : {text : '复仇者联盟4'}">热门电影1</router-link>
<router-link :to = "{name : 'hot' , params : {id : 2}, query : {text : '钢铁侠3'}}">热门电影2</router-link>
<div>
<router-view></router-view>
</div>
</div>
</template>
十一,JSON.parse()
用于将一个 JSON 字符串转换为对象。
十二,refs
this.$refs["input"].focus()
十三,vuex快速使用
状态管理
应用状态,修改状态
一。定义state(store.js)
new一个Store出来
import Vue from "vue";
import Vuex from "vuex";
Vue.use( Vuex ) ;
const store = new Vuex.Store( {
state : {
count : 0
}
} );
export default store;
创建了一个store,store.state 里定义了状态。
与在Vue里定义data没有任何区别
二。修改状态(直接修改,用点操作修改)
state.count = 2;
在严格模式下会报错,更改store中的状态的唯一方法应该是提交mutation。
2.1 严格模式
开启严格模式,仅需要在创建store的时候传入 strict: true
const store = new Vuex.Store( {
strict : true
} );
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。
mutation
更改Vuex的store中的状态的唯一方法是提交mutation。
在Vuex.Store的构造器选项中,有一个mutation选项,这个选项就像是事件注册:key是一个字符串表示 mutation 的类型 (type),value 是一个回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,它有两个入参,第一个参数是 state,就是 store 里的 state;第二个参数是Payload(有效载荷),这是提交 mutation 时候额外传入的参数。
2.2 定义 mutation
const store = new Vuex.Store ({
state : {
count : 1
},
mutations : {
increment (state) {
state.count ++ ; //变更状态
}
}
});
2.3 提交 mutation
上面定义了 mutation ,要唤醒一个 mutation hanler(变异处理程序),唯一的接口是 store.commit,如果你熟悉事件监听,commit 就类似于Vue 的 $emit,jquery 的 trigger。
可想而知,commit 方法传参必须至少有一个能区分 mutation 的唯一标识,这个标识就是 type。
commit 参数是对象
当 commit 的参数是一个对象的时候,对象里必须要有 type 字段,除了 type 字段,你还可以添加额外任意字段为载荷(payload)。
//对象风格的提交方式
store.commit ({
type : "increment",
amount : 10
});
type 做第一参数
把 type 和 payload 分开也是个不错的选择,可以把 type 单独拿出来当第一个参数,以 commit ( type, [payload] ) 的形式提交,官方称此为以载荷形式提交。
store.commit ("increment");
store.commit ("increment", 10);
store.commit ("increment", { count : 2 });
在大多数情况下,payload 应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。
使用mutation,有两个地方用
1)Vue应用的组件里,在组件里调用 store.commit。
2)还有 store 的 action 里。
三。Action
状态管理不过就是定义 state 和修改 state,mutation 已经可以修改 state 了,为什么还需要 action ?
同步和异步
在 Vuex 中,mutation 都是同步事务。
当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
3.1 注册 action:
注册 action 就跟定义 mutation 一样,除了 handler (处理程序)的入参不同。
Action 函数的入参是一个与 store 实例具有相同方法和属性的 context(语境) 对象。
一。context.commit 提交一个 mutation,
二。context.state获取 state。
三。context.getters 获取 getters。
const store = new Vuex.Store ({
state : {
count : 0
},
mutations : {
increment (state) {
state.count ++;
}
},
actions : {
increment (context) {
context.commit ("increment");
}
}
});
3.2 分发Action
Action 通过 store.dispatch 方法触发:
//以载荷形式分发
store.dispatch("incrementAsync", {
amount : 10
})
//以对象形式分发
store.dispatch ({
type : "incrementAsync",
amount : 10
})
3.3 组合Action
store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且store.dispatch 仍旧返回 Promise,因此,通过 async / await,很方便控制流程
actions : {
async actionA ( { commit } ) {
commit ( 'gotData' , await getData() )
},
async actionB ( {dispatch , commit } ) {
await dispatch ('actionA') //等待 actionA 完成
commit ('gotOtherData' , await getOtherData() )
}
}
3.4 表单问题
表单的问题在于使用 v-model 时候,v-model 会试图直接修改 state,而 Vuex 在严格模式下是不允许直接修改 state 的。
很容易解决,只要我们把修改 state 的行为按 Vuex 的要求以 commit mutation 方式修改即可。
有两种方式。
1. 用 “Vuex” 的思维解决
抛弃 v-model 指令,自已去实现 v-model 双向绑定,非常简单,
一。input 标签的 value 属性绑定对应从 store 中映射来的计算属性,
二。监听 input 事件,用 commit mutation 的方式,修改 store 里的 state。
<input :value = "message" @input = "updateMessage" />
computed : {
...mapState ({
message : state => state.obj.message
})
},
methods : {
updateMessage (e) {
this.$store.commit ('updateMessage' , e.target.value)
}
store 中的 mutation 函数 :
mutations :{
updateMessage ( state , message ) {
state.obj.message = message
}
}
}
2. 用 Vue 计算属性解决
如果坚持使用 v-model,可以在Vue 里对 v-model 绑定的计算属性设置 set 行为。
<input v-model = "message" />
computed : {
message : {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit ( 'updateMessage' , value )
}
}
}
使用 Vuex 进行状态管理,到此结束。
从 flux 架构来看,三个核心,State ,Mutation,Action ,已经足够了,总结一下其实很简单,同步 commit mutation,异步 dispatch action。
四。核心原则
一。应用层级的状态应该集中到单个 store 对象中。
二。提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
三。异步逻辑都应该封装到 action 里面。
五。Vue 应用使用 Vuex
Vuex 的常用 api 上面都涉及到了,使用思路就是:
一,定义状态:通过 new Vuex.Store ({}) 创建一个 store 实例,定义 state,getters,actions,mutations。
二,改变状态:使用 store.commit 提交 mutation,使用 store.dispatch 分发 actions。
现在状态管理的部分已经全部由 store 实例去管理了,如何在 Vue 组件中使用 store:
在 store.js 文件里我们创建了 store 实例并将其导出了(export),按照 js 模块化的知识,我们只要在需要的地方导入它就可以直接使用了。
组件引入 store:
可以在需要的组件里直接引入,就像引入一个普通的 js 一样:
import store from "path/to/store.js";
组件中引入了 store ,就可以直接通过 store.state 引用 state,以及直接使用 state 实例简洁的 api:
store.state.count = 2 ;//直接修改,并不建议
store.commit () ;//在 store 中调用 mutation
store.dispatch ( "increment" ); //在 store 中分发 action
我们往往需要在 Vue 组件的 template 中展示状态,由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:
import store from "path/to/store.js" ;
const Counter = {
template : `<div>{{count}}</div>`,
computed : {
count () {
return store.state.count
}
}
};
每当 store.state.count 变化的时候,都会重新求取计算属性,并且触发更新相关联的 DOM。
组件中引入的方式,缺点:这种模式导致组件依赖全局状态单例。在模块化的构件系统中,在每个需要使用 state 的组件中需要频繁地导入。
三。全局引入 store
Vuex 通过 store 选项,提供了一种机制将状态从跟组件 ”注入“ 到每一个子组件中:
//app.js
import Vue from "vue";
import store from "path/to/store.js"
const app = new Vue ({
store //把 store 对象提供给 ”store“ 选项,这可以把 store 的实例注入所有的子组件
});
通过在 根实例 中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。这样我们就不用每个组件单独引入了。
this.$store.state.count = 2;//直接修改,并不建议
this.$store.commit ( "" ,{} );//直接在组件中提交 mutation
this.$store.dispatch ( "increment" ); //在组建中分发 action
四,组件绑定的辅助函数
1,mapState
2,mapGetters
3,mapMutations
4,mapActions
既然是辅助,就不是必须的东西,辅助函数可以方便的把 Vuex 中的 state,getter,mutation,action 映射到组件,方便调用。
一,更多概念
Getter
如果你很喜欢 Vue 提供的计算属性 (computed),Vuex 允许在 store 中定义 "getter"(可以认为是 store 的计算属性)。
但对状态管理来说,Getter 其实并不是必须的。
十四,vue插件 (手动裁剪图片) http://github.xyxiao.cn/vue-cropper/example/
npm install vue-cropper
// 组件内使用
import { VueCropper } from 'vue-cropper'
components: {
VueCropper,
},
// main.js里面使用
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
// cdn方式使用
<script src="vuecropper.js"></script>
Vue.use(window['vue-cropper'])
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
></vueCropper>
<div class="show-info">
<h2>example1 基本例子 无限制</h2>
<div class="test test1">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="true"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:fixedBox="option.fixedBox"
:original="option.original"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:centerBox="option.centerBox"
:high="option.high"
:infoTrue="option.infoTrue"
@realTime="realTime"
@imgLoad="imgLoad"
@cropMoving="cropMoving"
:enlarge="option.enlarge"
></vueCropper>
</div>
<div class="test-button">
<button @click="changeImg" class="btn">changeImg</button>
<label class="btn" for="uploads">upload</label>
<input type="file" id="uploads" style="position:absolute; clip:rect(0 0 0 0);" accept="image/png, image/jpeg, image/gif, image/jpg" @change="uploadImg($event, 1)">
<button @click="startCrop" v-if="!crap" class="btn">start</button>
<button @click="stopCrop" v-else class="btn">stop</button>
<button @click="clearCrop" class="btn">clear</button>
<button @click="refreshCrop" class="btn">refresh</button>
<button @click="changeScale(1)" class="btn">+</button>
<button @click="changeScale(-1)" class="btn">-</button>
<button @click="rotateLeft" class="btn">rotateLeft</button>
<button @click="rotateRight" class="btn">rotateRight</button>
<button @click="finish('base64')" class="btn">preview(base64)</button>
<button @click="finish('blob')" class="btn">preview(blob)</button>
<a @click="down('base64')" class="btn">download(base64)</a>
<a @click="down('blob')" class="btn">download(blob)</a>
<a :href="downImg" download="demo.png" ref="downloadDom"></a>
</div>
<p>截图框大小</p>
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<p>中等大小</p>
<div :style="previewStyle1">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<p>迷你大小</p>
<div :style="previewStyle2">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<div style="display:block; width: 100%;">
<label class="c-item">
<span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
<input type="checkbox" v-model="option.original">
<span>original: {{ option.original}}</span>
</label>
<label class="c-item">
<span>是否根据dpr生成适合屏幕的高清图片</span>
<input type="checkbox" v-model="option.high">
<span>high: {{ option.high}}</span>
</label>
<label class="c-item">
<span>是否输出原图比例的截图</span>
<input type="checkbox" v-model="option.full">
<span>full: {{ option.full}}</span>
</label>
<label class="c-item">
<span>截图信息展示是否是真实的输出宽高</span>
<input type="checkbox" v-model="option.infoTrue">
<span>infoTrue: {{ option.infoTrue}}</span>
</label>
<label class="c-item">
<span>能否拖动图片</span>
<input type="checkbox" v-model="option.canMove">
<span>canMove: {{ option.canMove}}</span>
</label>
<label class="c-item">
<span>能否拖动截图框</span>
<input type="checkbox" v-model="option.canMoveBox">
<span>canMoveBox: {{ option.canMoveBox}}</span>
</label>
<label class="c-item">
<span>截图框固定大小</span>
<input type="checkbox" v-model="option.fixedBox">
<span>fixedBox: {{ option.fixedBox}}</span>
</label>
<label class="c-item">
<span>是否自动生成截图框</span>
<input type="checkbox" v-model="option.autoCrop">
<span>autoCrop: {{ option.autoCrop}}</span>
</label>
<label class="c-item">
<span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
<input type="checkbox" v-model="option.centerBox">
<span>centerBox: {{ option.centerBox}}</span>
</label>
<label class="c-item">
<span>是否按照截图框比例输出 默认为1 </span>
<input type="number" v-model="option.enlarge">
</label>
<p>输出图片格式</p>
<label class="c-item">
<label>jpg <input type="radio" name="type" value="jpeg" v-model="option.outputType"></label>
<label>png <input type="radio" name="type" value="png" v-model="option.outputType"></label>
<label>webp <input type="radio" name="type" value="webp" v-model="option.outputType"></label>
</label>
</div>
<codes>
<div slot="body">{{ code1 }}</div>
</codes>
</div>
十五,在线JSON格式化
添加链接描述
十六,vue 2.0 watch
在 vue2.0 中,我们使用 watch 可以通过下面多种方式去监听 Vue 实例上面的表达式或者函数计算结果的变化,如下罗列了其中的几种:
一,最常规使用方式
export default {
data () {
return {
name : '翘翘红',
info : {
qqh : '前端有得玩'
}
}
},
watch :{
name ( newValue , oldValue ) {
console.log( newValue , oldValue )
},
'info.qqh' : {
handler (newValue , oldValue ) {
console.log( newValue , oldValue )
},
//配置 immediate 会在 watch 之后立即执行
immediate : true
}
}
}
我们可以在 watch 属性里面配置要监听的 Vue 实例上面的属性,也可以通过 . 键路径去监听对象中的某一个属性的变化,也可以通过配置 immediate 在监听后立即触发,配置 deep 去深度监听对象里面的属性,不论嵌套层级有多深。
二,使用 $watch 监听
除了常规的 watch 对象写法之外,Vue 实例上面提供了 $watch 方法,可以通过 $watch 更灵活的去监听莫一个属性的变化。比如这样一个场景,我们有一个表单,然后希望在用户修改表单之后可以监听到表单的数据变化。但是有一个特殊的场景,就是表单的回填数据是异步请求过来的,这时候我们希望在后台请求完数据之后再去监听变化,这时候就可以使用 $watch 。
export default {
methods : {
loadData () {
fetch ().then(data => {
this.formData = data
this.$watch (
'forData' ,
() => {
//formData 数据发生变化后会进入此回调函数
},
{
deep : true
}
)
})
}
}
}
三,监听函数表达式
除了监听字符串之外,$watch 的第一个参数也可以是一个函数,当函数的返回值发生变化之后,触发回调函数。
this.$watch( () => this.name, () => {
//函数的返回值发生变化,进入此回调函数
} )
上文中就是 vue2.0 中我们使用 watch 的一些常用写法,对于 vue3.0 ,因为其对 vue2.0 做了部分的向下兼容,所以上面的用法在 vue3.0 中基本都可以使用,但是 vue3.0 一个很大的亮点就是 composition API,所以除了 vue2.0 中的写法之外,也可以使用 componsition (组成)API,所以除了 vue2.0 中的写法之外,也可以使用 componsition api 中提供的 watch
四,在 vue3.0 中使用 watch
在 vue3.0 中,除了 vue2.0 的写法之外,有两个 api 可以对数据变化进行监听,第一种是 watch,第二种是 watchEffect(观察效果)。对于 watch,其与 vue2.0 中的 $watch 用法基本是一摸一样的,而 watchEffect 是 vue3.0 新提供的 api。
4.1,watch 的用法
import { watch, ref, reactive } from 'vue'
export default {
setup () {
const name = ref ( '子君' )
const otherName = reactive ({
firstName : '王',
lastName : '二狗'
})
watch ( name, ( newValue , oldValue ) => {
//输出 前端有得玩 子君
console.log( newValue , oldValue )
})
//watch 可以监听一个函数的返回值
watch (
() => {
return otherName.firstName + otherName.lastName
},
value => {
//当 otherName 中的 firstName 或者 lastName 发生变化时,都会进入这个函数
console.log( `我叫${ value }` )
}
)
setTimeout ( ()=> {
name.value = '前端有得玩'
otherName.firstName = '李'
},3000 )
}
}
watch 除了可以监听单个值或者函数返回值之外,也可以同时监听多个数据源
export default {
setup () {
const name = ref (' 翘翘红 ')
const qqh = ref (' 前端有得玩 ')
watch( [ name, qqh ] , [ name, qqh ], [ preName, prevQQH ] ) => {
console.log( preName , name )
console.log( preQQH , qqh )
})
setTimeout( ()=> {
name.value = '前端有得玩',
name.value = '关注我,一起玩前端'
},3000 )
}
}
4.2 watchEffect 的用法
watchEffect 的用法与 watch 有所不同,watchEffect 会传入一个函数,然后立即执行这个函数,对于函数里面的响应式依赖会进行监听,然后当依赖发生变化时,会重新调用传入的函数
import { ref , watchEffect } from 'vue'
export default {
setup () {
const id = ref ( '0' )
watchEffect ( ()=> {
//先输出 0 然后两秒后输出 1
console.log( id.value )
} )
setTimeout ( () => {
id.value = '1'
},2000 )
}
}
1,停止执行
vue2.0 中的 $watch 会在调用的时候返回一个函数,执行这个函数可以停止 watch
const unwatch = this.$watch( 'name',() => {} )
//两秒后停止监听
setTimeout ( () => {
unwatch()
} ,2000)
在 vue3.0 中,watch 与 watchEffect 同样也会返回一个 unwatch 函数,用于取消执行
export default {
setup () {
const id = ref ( ' 0 ' )
const unwatch = watchEffect ( ()=> {
//仅仅输出0
console.log( id.value )
} )
setTimeout ( ()=> {
id.value = ' 1 '
} ,2000)
//1秒后取消watch,所以上面的代码只会输出0
setTimeout ( ()=> {
unwatch()
} ,1000)
}
}
2,清除副作用
想象一下这样的一个场景,界面上面有两个下拉框,第二个下拉框的数据是根据第一个下拉框的数据联动的,当第一个下拉框数据发生变化时,第二个下拉框的数据会通过发送一个网络请求进行获取。这时候我们可以通过 watchEffect 来实现这个功能
import { ref , watchEffect } from ' vue '
function loadData ( id ) {
return new Promise ( resolve => {
setTimeout ( () => {
resolve (
new Array( 10 ).fill( 0 ).map ( () => {
return id.value + Math.random()
} )
)
},2000 )
} )
}
export default {
setup () {
//下拉框 1 选中的数据
const select1Id = ref ( 0 )
//下拉框 2 的数据
const select2List = ref ( [ ] )
watchEffect ( () => {
//模拟请求
loadData ( select1Id) .then ( data => {
select2List . value = data
console.log ( data )
} )
} )
//模拟数据发生变化
setInterval ( () => {
select1Id . value = 1
} ,3000)
}
}
现在假如我切换了一下第一个下拉框的数据之后,这时候数据请求已经发出,然后我将这个页面切换到另一个页面,因为请求已经发出,所以我希望在页面离开的时候,可以结束这个请求,防止数据返回后出现异常,这时候就可以使用 watchEffect 为第一个回调函数传入的入参来处理这个情况。
function loadData ( id , cb ) {
return new Promise ( resolve => {
} )
}
十七,vue项目,多个组件使用调用一个方法
在src/mixins/mixin.js写入方法
import { Toast } from "vant";
export default function () {
return {
data() {
return {
}
},
filters: {
},
methods: {
checkUserPayment(callback) {
let _this = this
let requestData = {
reqHead: {
userId: this.$shareStorageMsg.userId
},
interfaceId: "49",
data: {}
};
this.$rpc("", requestData).then(res => {
// authFlag 01-指纹
// 02-手势
// 03-人脸
// openFlag 0-开启
// 1-关闭
// authUse 01-登录
// 02-动账
// (通常手势只用于登录)
if (res.code === 0) {
return callback && callback(res.data.fastAuthResult);
} else {
Toast(res.msg);
}
}).catch(err => {
console.log("失败:" + JSON.stringify(error));
Toast(err)
})
},
},
},
created() {
},
}
}
这个方法在多个组件调用
在组件中调用
import mixin from "@/mixins/mixin";
export default {
mixins: [mixin()],
data() {
return {
checked: false,
checkFingerData: {}, //检查指纹数据
}
},
methods:{
isChecked() {
this.checkUserPayment(res => {
console.log(res, "指纹管理查询")
res.forEach((item => {
if (item.authFlag == "01" && item.openFlag == "0" && item.authUse == "01") {
this.checked = true
}
}))
});
},
}
}
十八,js实现将不同路径下的图片转为base64编码
由于浏览器安全策略的限制,javascript程序不能自由地访问本地资源,这是对用户信息安全来说,是一项不得不遵守的准则。假如网络上的javascript程序可以自如地访问用户主机的本地资源,那么浏览器用户将毫无安全可言。尽管有这个安全限制,但是在得到用户允许的情况下,浏览器还是可以访问本地资源的。
[https://blog.csdn.net/gm0125/article/details/100087976](https://blog.csdn.net/gm0125/article/details/100087976)
获得用户允许的方法就是通过标签来让用户手动选择文件,这一过程就是用户授予访问权限的过程。
一,
<img id="img" src="" alt="" />
<img id="ssss" src="https://img10.360buyimg.com/mobilecms/s140x140_jfs/t17458/302/1792090017/223266/7f72f23e/5ad851cdN50df02d5.jpg!q90.webp" alt="" />
<script>
var img = new Image();
console.log(img)
img.crossOrigin = ""; //跨域
// document.body.appendChild(img)
// img.src = $('#img').attr('src');
img.src = document.getElementById("ssss").getAttribute('src');
img.onload = function() { // 图片加载后执行的方法
alert(9)
var base64 = getBase64Image(img); // 将图片转成base64格式的方法
// $('#img').attr('src', base64); // 将原来的图片src改为base64格式的地址
document.getElementById("img").setAttribute('src',base64) // 将原来的图片src改为base64格式的地址
}
function getBase64Image(img) {
var canvas = document.createElement("canvas"); // 创建一个canvas
canvas.width = img.width; // 设置对应的宽高
canvas.height = img.height;
var ctx = canvas.getContext("2d"); // 二维绘图环境
ctx.drawImage(img, 0, 0, img.width, img.height); // 将图片画在画布上
var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase(); // 获取到图片的格式
var dataURL = canvas.toDataURL("image/" + ext); // 得到base64 编码的 dataURL
console.log(dataURL)
return dataURL;
}
二,
情况二: 将外网服务器上的图片,转为base64,方法2
<img id='lo' style="border: 2px solid salmon; width: 200px; height: 200px;" src=""/ >
<script>
var surl = 'https://img10.360buyimg.com/mobilecms/s140x140_jfs/t17458/302/1792090017/223266/7f72f23e/5ad851cdN50df02d5.jpg!q90.webp'
converImg(surl, function(base64Img) {
//转化后的base64
alert(base64Img);
document.getElementById('lo').setAttribute('src', base64Img)
});
function converImg(url, callback, outputFormat) {
var canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
img = new Image;
// img.crossOrigin = 'Anonymous';
img.crossOrigin = '';
img.onload = function() {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
canvas = null;
};
img.src = url;
}
</script>
三,
情况三: 用户通过input[type='file'],将图片 转为base64
<input type="file" onchange="selectImage(this.files)" id="imgSel" value="" />
<img id="new" src="" style="width: 40px; height: 40px;border: 1px dashed #000;" />
<script>
function selectImage(e) {
let upload = document.getElementById("imgSel"); //input标签type=file
var reader = new FileReader(); //创建一个字符流对象
var AllowImgFileSize = 2100000; //上传图片最大值(单位字节)( 2 M = 2097152 B )超过2M上传失败
console.log(upload.files[0]);
reader.readAsDataURL(upload.files[0]); //读取本地图片
reader.onload = function(e) {
if (AllowImgFileSize != 0 && AllowImgFileSize < reader.result.length) {
alert( '上传失败,请上传不大于2M的图片!');
return;
}else{
//执行上传操作
alert(this.result); //返回数据
console.log(this.result)
let newa = document.getElementById("new"); //不能命名为new,因为new为关键字
newa.setAttribute("src", this.result)
}
}
}
</script>
补充:当不加 img.crossOrigin = '' 或者 img.crossOrigin = 'Anonymous' 时,我们使用的淘宝服务器上的一张图片,在本地服务器下访问,会出现图片跨域的问题;我们可以把图片放在本地服务器下即可解决上面的跨域问题,比如我现在将淘宝服务器下的图片保存在本地服务器下;如下代码即可解决 var img = "http://127.0.0.1/base64/1.jpg";
十九,使用gulp构建前端项目
1、npm -v
2、全局安装gulp npm install gulp@3 -g
3、gulp -v
4、创建一个项目文件夹, 当前项目文件夹下输入命令npm init -y
5、局部安装 gulp npm i gulp@3 -s-d
npm install gulp --save-dev
6、创建 gulpfile.js 文件,并且配置相关 gulp任务。
配置gulpflie.js,文件在当前项目文件下创建文件名为gulpfile.js文件, 作为该项目配置文件
const gulp = require('gulp'),
htmlmin = require('gulp-htmlmin'), // 压缩html文件
uglify = require('gulp-uglify'), // 压缩js文件
babel = require('gulp-babel'), // ES6转ES5
sass = require('gulp-sass'), // sass编译
cleanCss = require('gulp-clean-css'), // 压缩css文件
connect = require('gulp-connect') // 实时更新目录:当src中有需要文件发生修改时,更新dist的相关文件
// 制定html任务:把html压缩之后放到dist目录里
gulp.task('html', () => {
gulp.src('src/**/*.html')
.pipe(htmlmin({
removeComments: true,//清除HTML注释
collapseWhitespace: true,//压缩HTML
collapseBooleanAttributes: true,//省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: true,//删除所有空格作属性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: false,//删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true,//删除<style>和<link>的type="text/css"
minifyJS: true,//压缩页面JS
minifyCSS: true//压缩页面CSS
}))
.pipe(gulp.dest('dist'))
.pipe(connect.reload())
})
// 制定js任务:ES6转ES5,再压缩js
gulp.task('js', () => {
gulp.src('src/js/**/*.js')
.pipe(babel({ // ES6 => ES5
presets: ['@babel/env']
}))
.pipe(uglify()) // js代码压缩, 压缩了是真的丑(为了方便调试,在没有真正上线之前,可以不带有这句代码)
.pipe(gulp.dest('dist/js')) // 压缩目录
.pipe(connect.reload())
})
// css任务:先把scss编译成css,压缩css
gulp.task('css', () => {
gulp.src('src/css/**/*.scss')
.pipe(sass()) // sass 编译
.pipe(cleanCss()) // css样式压缩
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload())
})
// img任务:移动图片
gulp.task('img', () => {
gulp.src('src/images/**/*')
.pipe(gulp.dest('dist/images'))
})
// libs任务:移动文件
gulp.task('libs', () => {
gulp.src('src/libs/**/*')
.pipe(gulp.dest('dist/libs'))
})
// server任务:开启一个本地服务器
gulp.task('server', () => {
connect.server({
root: 'dist',
port: 2333,
livereload: true
})
})
// watch任务:监听文件的修改,执行对应的任务
gulp.task('watch', () => {
gulp.watch('src/**/*.html', ['html'])
gulp.watch('src/js/**/*.js', ['js'])
gulp.watch('src/css/**/*.scss', ['css'])
})
// 把所有任务放进default里,默认全部执行一次
gulp.task('default', ['html', 'js', 'css', 'img', 'libs', 'server', 'watch'])
7、安装相关依赖包 npm i gulp-htmlmin gulp-uglify gulp-sass gulp-clean-css gulp-connect gulp-babel @babel/core @babel/preset-env -s-d
由于在gulpfile.js中已经配置了default任务,所以可以直接在gulpfile.js同级目录下启动终端,输入gulp运行项目,然后看缺少什么包引入什么包(虽然有点low,但是很实用)。
8、第一次启动 gulp
在终端直接输入 gulp 启动gulp-default任务,如果出现以下提示信息,并且在浏览器里输入相应的网址,能成功访问!恭喜你,可以开始通宵加班了!!!
- 第一次执行gulp会创建dist目录
在终端直接输入 gulp 启动gulp-default任务,如果出现以下提示信息,并且在浏览器里输入相应的网址,能成功访问!恭喜你,可以开始通宵加班了!!!
- 第一次执行gulp会创建dist目录