2021-03-10 整理笔记

笔记

一,
要使用一个组件里面的方法

<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目录


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值