Vue笔记(有点乱)

Vue学习笔记(2019.7.31)

目录
  • Vue学习笔记(2019.7.31)
    • vue
      • 基本指令用法
        • v-cloak v-text v-html
        • v-bind v-on
        • 跑马灯
        • v-on
        • v-model
        • class
        • v-for key
        • v-if v-show
        • filter,filters
        • 键盘修饰符
        • directive
      • vue的生命周期
      • vue-resource
      • Vue中的动画
      • vue组件
        • 组件切换
          • v-if v-else
          • component:is
        • 父子组件传值
        • 兄弟之间的传递
        • 组件插槽
          • 匿名插槽
          • 具名插槽
          • 作用域插槽
          • 购物车案例
        • this.$refs
      • vue-router
        • 路由传参
        • 路由嵌套
        • 命名路由
        • 编程式导航
        • 命名视图
      • watch computed
        • 计算属性缓存 vs 方法
    • webpack
      • 安装
      • 配置
      • webpack-dev-server
      • html-webpack-plugin
      • webpack-dev-server配置项
      • 打包其他类型文件
        • 打包css、less、sass:
        • 自动添加浏览器前缀postcss
        • 处理css中的路径:
        • 处理高级JS语法:
        • 处理vue:
      • 构建Vue项目
      • webpack完整配置:
      • webpack中vue-router
    • 使用第三方组件
      • 使用MintUI
      • 使用MUI
      • 使用boostrap
    • 中间组件的切换动画
    • vue中的vue-resource
    • mui的tab-top-webview-main分类滑动栏问题
    • lazyload,vue-preview
    • 真机调试
    • vue-router编程式导航
    • 抽离轮播图,小球动画,传值问题
      • 抽离轮播图组件
      • 小球动画
      • numberbox子组件传值给父组件
      • 当父组件要传递给子组件的值是异步请求的值时需要子组件用watch去监听传递过来的值
    • vuex 优化购物车
    • vuex
      • state
      • getters
      • mutation
      • Action
      • module

vue

基本指令用法

v-cloak v-text v-html

  • v-cloak解决插值表达式闪烁问题
[v-cloak] {
	display: none; 
}
  • v-text会覆盖元素中原本的内容,但是插值表达式只会替换自己的这个占位符,不会把整个元素的内容清空。
  • v-html,带有html标签的字符串
1212112

v-bind v-on

  • v-bind指令可以被简写为 :要绑定的属性,v-bind 中,可以写合法的JS表达式。

  • v-on

跑马灯

         lang() {
          if (this.intervalId != null) return;
          this.intervalId = setInterval(() => {
            var start = this.msg.substring(0, 1)
            var end = this.msg.substring(1)
            this.msg = end + start
          }, 400)

          // 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
        },
        stop() { // 停止定时器
          clearInterval(this.intervalId)
          // 每当清除了定时器之后,需要重新把 intervalId 置为 null
          this.intervalId = null;
        }

v-on

  • .stop 阻止冒泡
  • .prevent 阻止默认事件
  • .capture 添加事件侦听器时使用事件捕获模式
  • .self 只当事件在该元素本身(比如不是子元素)触发时触发回调 (只会阻止自己身上冒泡行为的触发,并不会真正阻止 冒泡的行为)
  • .once 事件只触发一次

v-model

  • v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定

  • 使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定

  • v-model 只能运用在 表单元素中

  

{{ msg }}

class

  1. 数组
:class="['red', 'thin']"
  1. 数组中使用三元表达式
:class="['red', 'thin', isactive?'active':'']"
  1. 数组中嵌套对象
:class="['red', 'thin', {'active': isactive}]"

  1. 直接使用对象
:class="{'red':true,'thin':true,'active':isactive}"

内联样式:

直接在元素上通过 :style 的形式,书写样式对象

:style="{color: 'red', 'font-size': '40px'}"

将样式对象,定义到 data 中,并直接引用到 :style

v-for key

  1. 数组
v-for="(item, i) in list

  1. 迭代对象中的属性
v-for="(val, key, i) in userInfo

  1. 数字
v-for="i in 10"

当在组件中使用** v-for 时,key 现在是必须的。

v-if v-show

  • v-if 的特点:每次都会重新删除或创建元素

  • v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式

如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show,如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if

filter,filters

  1. 私有过滤器
{{item.ctime | dataFormat('yyyy-mm-dd')}}

定义方式:

filters: { 
	dataFormat(input, pattern = ""){
		var dt = new Date(input);
      	// 获取年月日
     	var y = dt.getFullYear();
     	var m = (dt.getMonth() + 1).toString().padStart(2, '0');
      	var d = dt.getDate().toString().padStart(2, '0');

	...
	}
}

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串;

  1. 全局过滤器
Vue.filter(
		'dataFormat', 
		function (input, pattern = '') {
		...
		}

注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

键盘修饰符

定义:

Vue.config.keyCodes.f2 = 113;

使用自定义的按键修饰符:



directive

  1. 全局自定义指令:
Vue.directive(
		'focus', {
      	inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
        el.focus();
      }
      });

  1. 自定义局部指令:
 directives: {
        color: { 
          bind(el, binding) {
            el.style.color = binding.value;
          }
        },
        'font-weight': function (el, binding2) { 
        // 自定义指令的简写形式,等同于定义了 bind 和 	update 两个钩子函数
          el.style.fontWeight = binding2.value;
        }
      }

使用方式:



vue的生命周期

  • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性

  • created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

  • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中

  • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

  • 运行期间的生命周期函数:

  • beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点

  • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

  • 销毁期间的生命周期函数:

  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

  • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

vue-resource

  1. 发送get请求:
this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
    console.log(res.body);
  })

  1. 发送post请求:
this.$http.post(url,
				{ name: 'zs' },
                { emulateJSON: true}).then(res => {
    	console.log(res.body);
  });

全局配置接口和emulateJSON 选项:

  • Vue.http.options.root = 'http://vue.studyit.io/';

注意:发起请求的前面不要加/了 this.$http.get('api/getprodlist').then

  • Vue.http.options.emulateJSON = true;

Vue中的动画

  1. 使用过渡类名
  • v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入

  • v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了

  • v-enter-active 【入场动画的时间段】

  • v-leave-active 【离场动画的时间段】

    .v-enter,
    .v-leave-to {
      opacity: 0;
      transform: translateX(150px);
    }
    
    .v-enter-active,
    .v-leave-active{
      transition: all 0.8s ease;
    }


    
      

这是一个H3

transition后可加name属性 修改动画的v-前缀

  1. 使用第三方 CSS 动画库

先导入动画类库:



再定义 transition 及属性:


  	
动画哦
  • enter-active-class

  • leave-active-classs

  • :duration=''{enter: , leave: }'

  • animated

  1. 使用动画钩子函数

      
OK

定义三个 methods 钩子方法:

methods: {
        beforeEnter(el) { // 动画进入之前的回调
          el.style.transform = 'translateX(500px)';
        },
        enter(el, done) { // 动画进入完成时候的回调
          el.offsetWidth;
          el.style.transform = 'translateX(0px)';
          done();
        },
        afterEnter(el) { // 动画进入完成之后的回调
          this.isshow = !this.isshow;
        }
      }

  1. v-for 的列表过渡

transition-group 组件把v-for循环的列表包裹起来:

    
      
  • {{item}}
    • transition-group 添加 appear 属性,实现页面刚展示出来时候,入场时候的效果

    • 通过为 transition-group 元素,设置 tag 属性,指定 transition-group 渲染为指定的元素

    下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐地漂上来的效果

    .v-move{
      transition: all 0.8s ease;
    }
    .v-leave-active{
      position: absolute;
    }
    
    

    vue组件

    1. 使用 Vue.extend 配合 Vue.component 方法:
    var login = Vue.extend({
          template: '

    登录

    ' }); Vue.component('login', login);
    1. 直接使用 Vue.component 方法:
    Vue.component('register', {
          template: '

    注册

    ' });
    1. 字面量方式
    var login= {
    	template:#apl
    	data(){
    		return {}
    	}
    }
    
    
    vue.component('login', login)
    //或者挂在到vm的components上
    
    

    组件切换

    v-if v-else
     
        
        
    
    
    component:is

    使用component标签,来引用组件,并通过:is属性来指定要加载的组件:

    注意:组件名称是字符串

     
    
    

    父子组件传值

    1. 父向子传值

      • 父组件定义子组件标签时bind简写绑定过去:
      < son :finfo="msg">
      
      

      再在子组件中定义props属性(注意是数组)

      props: ['finfo']
      
      
    2. 子向父传值(借用父向字传递方法)

      • 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
      < son @func="getMsg">
      
      
      • 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
      this.$emit('func', data)
      
      

    兄弟之间的传递

    • 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
      • 提供事件中心 var hub = new Vue()
    • 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
    • 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
    • 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
     
    父组件

    地存储获取时需注意:

    JSON.parse(localStorage.getItem(' cmts') || '[]')
    
    localStorage.setItem('cmts', JSON.stringify(list))
    
    

    组件插槽

    • 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
    匿名插槽
      
    有bug发生 有一个警告
    具名插槽
    • 具有名字的插槽
    • 使用 < slot >中的 "name" 属性绑定元素
     

    标题信息

    主要内容1

    主要内容2

    底部信息信息

    主要内容1

    主要内容2

    作用域插槽
    • 父组件对子组件加工处理
    • 既可以复用子组件的slot,又可以使slot内容不一致
    购物车案例

    this.$refs

    定义:ref="mycom"

    获取:this.$refs.mycom.innerText

    vue-router

    1. 使用 router-link 组件来导航

      < router-link to="/login">登录< /router-link>

    2. 使用 router-view 组件来显示匹配到的组件

      < router-view>< /router-view>

    3. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则

     var router = new VueRouter({
          routes: [
          	{ path: '/', redirect: '/login' },
            { path: '/login', component: login },
            { path: '/register', component: register }
          ]
        });
    
    
    1. 再挂载到vm实例上

    路由传参

    1. restful风格:
    const router = new VueRouter({
        routes: [
           { path: '/register/:id', component: register } 
        ]
    })
    
    

    通过this.$route.params获取

    1. url地址传参
     登录
    
    

    通过this.$route.query.id获取

    1. props属性
    const router = new VueRouter({
        routes: [
          { path: '/register/:id', component: register, props: true}
        ]
    })
    
    const register = {
        props: ['id']
        template: `
    {id}
    ` }

    路由嵌套

        cost router = new VueRouter({
          routes: [
            {
              path: '/account',·
              component: account,
              children: [
                { path: 'login', component: login },
                { path: 'register', component: register }
              ]
            }
          ]
        })
    
    

    使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址

    命名路由

    const router = new VueRouter({
        routes: [
          { path: '/register/:id',
           	component: register, 
           	name: register
          }
        ]
    })
    
    使用:
    
    router.push({name: register, params: {id: 123}})
    
    

    编程式导航

    router.push('/home')
    router.push({path: '/home'})
    router.push({name: register, params: {id: 123}})
    router.push({path: '/register', query: {uname: 'lisi'}})
    
    

    命名视图

    var router = new VueRouter({
          routes: [
            {
              path: '/', components: {
                'default': header,
                'left': leftBox,
                'main': mainBox
              }
            }
          ]
        })
    
    

    watch computed

    1. watch:
    watch: {
            'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
              this.fullName = newVal + ' - ' + this.lastName;
            },
            '$route': function (newVal, oldVal) {
              if (newVal.path === '/login') {
                console.log('这是登录组件');
              }
            }
    
    
    1. computed:

      computed: { 
          // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发本计算	属性 的重新计算,从而更新 fullName 的值fullName() {
          return this.firstName + ' - ' + this.lastName;
        }
      
      

    注意: 只要 计算属性,这个 function 内部,所用到的 任何 data 中的数据发送了变化,就会立即重新计算 这个 计算属性的值

    计算属性缓存 vs 方法

    Reversed message: "{{ reversedMessage() }}"

    // 在组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } }

    我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

    这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

    computed: {
      now: function () {
        return Date.now()
      }
    }
    
    

    webpack

    安装

    方式一:

    1. 运行npm i webpack -g全局安装webpack,这样就能在全局使用webpack的命令
    2. 在项目根目录中运行npm i webpack --save-dev安装到项目依赖中

    方式二:

    安装webpack:npm install webpack webpack-cli -D

    执行命令:

    1. webpack src/js/main.js dist/bundle.js
    
    2. webpack 
    
    

    单独使用webpack命令,webpack默认打包src目录里的index.js到dist目录的main.js

    配置

    1. 在项目根目录中创建webpack.config.js

      var path = require('path');
      module.exports = {
          entry: path.join(__dirname, 'src/js/main.js'),
          output: { 
              path: path.join(__dirname, 'dist'), 
              filename: 'bundle.js' 
          }
      }
      
      
    2. 修改项目中的package.json文件添加运行脚本dev

      "scripts":{
              "dev":"webpack"
       }
      
      
    3. 运行dev命令进行项目打包:npm run dev

    4. 在页面中引入项目打包之后在dist文件夹中生成的js文件

      
      
      

    webpack-dev-server

    作用:webpack自动打包

    • npm i webpack-dev-server -D

    • package.json文件中的指令,来进行运行webpack-dev-server命令,在scripts节点下新增:

    "scripts":{
    	"dev":"webpack-dev-server"
    }
    
    

    此时需要修改index.html中script的src属性为:

    
    
    
    • 为了能在访问http://localhost:8080/的时候直接访问到index首页,可以使用--contentBase src指令来修改dev指令,指定启动的根目录:
     "dev": "webpack-dev-server --contentBase src"
    
    

    html-webpack-plugin

    由于使用--contentBase指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin插件配置启动页面.

    1. 运行npm i html-webpack-plugin -D安装到开发依赖
    2. 修改webpack.config.js配置文件如下:
    	var path = require('path');
        var htmlWebpackPlugin = require('html-webpack-plugin');
    
        module.exports = {
            entry: path.resolve(__dirname, 'src/js/main.js'), 
            output: { 
                path: path.resolve(__dirname, 'dist'), 
                filename: 'bundle.js' 
            },
            plugins:[ 
                new htmlWebpackPlugin({
                    template:path.join(__dirname, 'src/index.html'),
                    filename:'index.html'
                })
            ]
        }
    
    
    1. 修改package.jsonscript节点中的dev指令如下:
    "dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888"
    
    

    注意:html-webpack-plugin插件会自动把bundle.js注入到index.html页面中,所以可以不用添加引入bundle.js的script标签

    webpack-dev-server配置项

    方式1:

    "dev": "webpack-dev-server --hot --port 4321 --open"
    
    

    方式2:

    1. 修改webpack.config.js文件,新增devServer节点如下:
    devServer:{
            hot:true,
            open:true,
            port:4321
        }
    
    
    1. 在头部引入webpack模块:
    var webpack = require('webpack');
    
    
    1. plugins节点下新增:
    new webpack.HotModuleReplacementPlugin()
    
    

    打包其他类型文件

    1. 安装相关打包需要工具

    打包css、less、sass:

    npm i style-loader css-loader -D
    npm i less-loader less -D
    npm i sass-loader node-sass -D
    
    

    配置webpack.config.js的module中的rules数组:

    module.exports = {
            ......
            module : {
                rules:[
                    {   test:/\.css$/, use:['style-loader','css-loader']  },
                    {   test:/\.less$/,use:['style-loader','css-loader','less-loader']  },
                    {   test:/\.scss$/,use:['style-loader','css-loader','sass-loader']   }
                ]
            }
        }
    
    

    自动添加浏览器前缀postcss

    npm i postcss-loader autoprefixer -D
    
    

    创建并配置postcss.config.js文件:

    const autoprefixer = require("autoprefixer");
    module.exports = {
        plugins:[ autoprefixer ]
    }
    
    

    配置更改webpack.config.js的module中的rules数组:

    module.exports = {
        ......
        plugins:[ htmlPlugin ],
        module : {
            rules:[
                ..........
                {  test:/\.css$/, use:['style-loader','css-loader','postcss-loader']  }
            ]
        }
    }
    
    
    

    处理css中的路径:

    npm i url-loader file-loader -D
    
    

    配置webpack.config.js的module中的rules数组:

    module.exports = {
            ......
            module : {
                rules:[
                            .......
    	{  test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
                               use:"url-loader?limit=16940"  }
                ]
            }
        }
    
    

    处理高级JS语法:

    //转换器相关包
    npm i babel-loader @babel/core @babel/runtime -D
    
    //语法插件包
    npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
    
    

    在项目根目录创建并配置babel.config.js文件:

    module.exports = {
            presets:["@babel/preset-env"],
            plugins:[ "@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties" ]
        }
    
    

    配置webpack.config.js的module中的rules数组:

    rules:[
    	{  test:/\.js$/,  use:"babel-loader",  exclude:/node_modules/  }
    ]
    
    

    处理vue:

    npm i vue-loader vue-template-compiler -D
    
    

    配置规则:更改webpack.config.js的module中的rules数组:

        const VueLoaderPlugin = require("vue-loader/lib/plugin");
       	 	module.exports = {
            plugins:[ htmlPlugin, new VueLoaderPlugin()],
            module : {
                rules:[
                    ......,
                    	{ test:/\.vue$/,loader:"vue-loader",   
                    }
                ]
            }
        }
    
    

    构建Vue项目

    npm install vue

    1. webpack.config.js中添加resolve属性:
    resolve: {
        alias: {
          'vue$': 'vue/dist/vue.js' //可以不添加
        }
      }
    
    
    1. 不修改,用 render()
    //import vue from '../node_modules/vue/dist/vue.js'
    import Vue from 'vue'
    // 导入 App组件
    import App from './components/App.vue'
    // 创建一个 Vue 实例,使用 render 函数,渲染指定的组件
    var vm = new Vue({
      el: '#app',
      render: c => c(App)
    });
    
    

    webpack完整配置:

    var path = require('path')
    var htmlWebpackPlugin = require('html-webpack-plugin')
    var webpack = require('webpack')
    
    module.exports = {
      entry: path.join(__dirname, './src/main.js'), // 入口文件
      output: { // 指定输出选项
        path: path.join(__dirname, './dist'), // 输出路径
        filename: 'bundle.js' // 指定输出文件的名称
      },
      plugins: [ // 所有webpack  插件的配置节点
        new htmlWebpackPlugin({
          template: path.join(__dirname, './src/index.html'), // 指定模板文件路径
          filename: 'index.html' // 设置生成的内存页面的名称
        }),
        new VueLoaderPlugin(),
        new webpack.HotModuleReplacementPlugin()
      ],
      module: { // 配置所有第三方loader 模块的
        rules: [ // 第三方模块的匹配规则
          { test: /\.css$/, use: ['style-loader', 'css-loader'] }, 
          { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, 
          { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
          { test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' }, 
          // limit 给定的值,是图片的大小,单位是 byte, 如果我们引用的 图片,大于或等于给定的 limit值,则不会被转为base64格式的字符串, 如果 图片小于给定的 limit 值,则会被转为 base64的字符串
          { test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, // 处理 字体文件的 loader 
          { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, // 配置 Babel 来转换高级的ES语法
          { test: /\.vue$/, use: 'vue-loader' } 
        ]
      },
      resolve: {
        alias: { // 修改 Vue 被导入时候的包的路径
          // "vue$": "vue/dist/vue.js"
        }
      }
    }
    
    

    webpack中vue-router

    1. 下载
    npm i vue-router
    
    
    1. main.js导入并引用
    import VueRouter from 'vue-router'
    Vue.use(VueRouter);
    
    
    1. 抽离router.js 导入需要展示的组件:
    import login from './components/account/login.vue'
    
    
    1. 创建路由对象:
    var router = new VueRouter({
      routes: [
    { path: '/', redirect: '/login' },
    
    { path: '/login', component: login },
    
    { path: '/register', component: register }
      ]
    
    });
    
    
    1. 挂载到vue实例上

    使用第三方组件

    使用MintUI

    首先下载:

    npm i mint-ui
    
    
    1. 完整引入:
    import MintUI from 'mint-ui'  //导入所以组件
    import 'mint-ui/lib/style.css' //可省略node_modules
    Vue.use(MintUI)
    
    //css组件可全局使用
    primary
    
    
    //组件中导入组件js组件要按需导入
    import { Toast } from 'mint-ui'
    //js中使用
    this.toastInstance = Toast({
        //配置项
    })
    
    //销毁Toast:
    this.toastInstace.close()
    
    
    1. 按需导入MintUI
      • 先下载:
    npm install babel-plugin-component -D
    
    

    然后将 .babelrc 修改为:

    {
      "presets": [
        ["es2015", { "modules": false }]
      ],
      "plugins": [["component", [
        {
          "libraryName": "mint-ui",
          "style": true
        }
      ]]]
    }
    
    

    再按需导入:

    import { Button, Cell } from 'mint-ui'
    
    Vue.component(Button.name, Button)
    Vue.component(Cell.name, Cell)
    
    

    使用MUI

    需手动下载,文档新建lib放入其中

    import '../lib/mui/css/mui.min.css'
    
    

    使用boostrap

    首先:

    npm install jquery  -D
    npm install bootstrap  -D
    
    

    webpack.config.js添加:

    plugins: [
            new webpack.ProvidePlugin({
              $: "jquery",
              jQuery: "jquery",
              "windows.jQuery": "jquery"    
    
     })
    
    

    最后导入使用:

    import $ from ‘jquery’
    import 'bootstrap/dist/css/bootstrap.css'
    import 'bootstrap/dist/css/bootstrap.min.js'
    
    

    中间组件的切换动画

    v-enter和v-leave-to要拆分来写 还需加绝对定位:

    .v-enter {
      opacity: 0;
      transform: translateX(100%);
    }
    
    .v-leave-to {
      opacity: 0;
      transform: translateX(-100%);
      position: absolute;
    }
    
    .v-enter-active,
    .v-leave-active {
      transition: all 0.5s ease;
    }
    
    
    

    vue中的vue-resource

    import VueResource from 'vue-resource'
    
    Vue.use(VueResource);
    
    

    mui的tab-top-webview-main分类滑动栏问题

    1. App.vue 中的 router-link 身上的类名 mui-tab-item 存在兼容性问题,导致tab栏失效,可以把mui-tab-item改名为mui-tab-item1,并复制相关的类样式,来解决这个问题;

    2. tab-top-webview-main`组件第一次显示到页面中的时候,无法被滑动的解决方案:

     import mui from '../../../lib/mui/js/mui.min.js'
    
    
    
    • 导入的 mui.js ,但是,控制台报错: Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode

    • 原因是 mui.js 中用到了 'caller', 'callee', and 'arguments' 东西,但是, webpack 打包好的 bundle.js 中,默认是启用严格模式的,所以,这两者冲突了;

    • 解决方案: 1. 把 mui.js 中的 非严格 模式的代码改掉;但是不现实; 2. 把 webpack 打包时候的严格模式禁用掉;需要在webpack.config.js的plugings节点添加如下:

    { "plugins":["transform-remove-strict-mode"]
    }
    
    

    在 组件的 mounted 事件钩子中,注册 mui 的滚动事件:

    mounted() {
            mui('.mui-scroll-wrapper').scroll({
              deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
            });
      	}
    
    
    1. 滑动的时候报警告:Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

    解决方法,可以加上 { touch-action: none; } 这句样式去掉。*

    lazyload,vue-preview

    1 mint-ui的lazyload按需导入有问题,只能全部导入所以组件

    image[lazy=loading] { width: 40px; height: 300px; margin: auto; }
    1. vue-preview的使用:
     
    
    
     getThumbs() {
          // 获取缩略图
          this.$http.get("api/getthumimages/" + this.id).then(result => {
            if (result.body.status === 0) {
              // 循环每个图片数据,补全图片的宽和高
              result.body.message.forEach(item => {
                item.w = 600;
                item.h = 400;
              });
              // 把完整的数据保存到 list 中
              this.list = result.body.message;
            }
          });
        }
      }
    
    

    真机调试

    1. 要保证自己的手机可以正常运行;

    2. 要保证 手机 和 开发项目的电脑 处于同一个 WIFI 环境中,也就是说 手机 可以 访问到 电脑的 IP

    3. 打开自己的 项目中 package.json 文件,在 dev 脚本中,添加一个 --host 指令, 把 当前 电脑的 WIFI IP地址, 设置为 --host 的指令值;

    • 如何查看自己电脑所处 WIFI 的IP呢, 在 cmd 终端中运行 ipconfig , 查看 无线网的 ip 地址

    vue-router编程式导航

    router.push('home')
    
    // 对象
    router.push({ path: 'home' })
    
    // 命名的路由
    router.push({ name: 'user', params: { userId: '123' }})
    
    // 带查询参数,变成 /register?plan=private
    router.push({ path: 'register', query: { plan: 'private' }})
    
    

    抽离轮播图,小球动画,传值问题

    抽离轮播图组件

    
    
    
    
    
    
    
    

    小球动画

    beforeEnter(el) {
          el.style.transform = "translate(0, 0)";
        },
        enter(el, done) {
          el.offsetWidth;
    
          // 小球动画优化思路:
          // 1. 先分析导致 动画 不准确的 本质原因: 我们把 小球 最终 位移到的 位置,已经局限在了某一分辨率下的 滚动条未滚动的情况下;
          // 2. 只要分辨率和 测试的时候不一样,或者 滚动条有一定的滚动距离之后, 问题就出现了;
          // 3. 因此,我们经过分析,得到结论: 不能把 位置的 横纵坐标 直接写死了,而是应该 根据不同情况,动态计算这个坐标值;
          // 4. 经过分析,得出解题思路: 先得到 徽标的 横纵 坐标,再得到 小球的 横纵坐标,然后 让 y 值 求差, x 值也求 差,得到 的结果,就是横纵坐标要位移的距离
          // 5. 如何 获取 徽标和小球的 位置???   domObject.getBoundingClientRect()
    
          // 获取小球的 在页面中的位置
          const ballPosition = this.$refs.ball.-();
          // 获取 徽标 在页面中的位置
          const badgePosition = document
            .getElementById("badge")
            .getBoundingClientRect();
    
          const xDist = badgePosition.left - ballPosition.left;
          const yDist = badgePosition.top - ballPosition.top;
    
          el.style.transform = `translate(${xDist}px, ${yDist}px)`;
          el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)";
          done();
        },
        afterEnter(el) {
          this.ballFlag = !this.ballFlag;
        }
    
    

    numberbox子组件传值给父组件

    //父组件:
    getSelectedCount(count) {
    	// 当子组件把 选中的数量传递给父组件的时候,把选中的值保存到 data 上
    	this.selectedCount = count;
    	console.log("父组件拿到的数量值为: " + this.selectedCount);
    }
    
    //子组件:
    countChanged() {
          // 每当 文本框的数据被修改的时候,立即把 最新的数据,通过事件调用,传递给父组件
          // console.log(this.$refs.numbox.value);
          this.$emit("getcount", parseInt(this.$refs.numbox.value));
        }
    
    

    当父组件要传递给子组件的值是异步请求的值时需要子组件用watch去监听传递过来的值

    父组件要传递给子组件的值是异步请求的值
    watch: {
        // 属性监听
        max: function(newVal, oldVal) {
          // 使用 JS API 设置 numbox 的最大值
          mui(".mui-numbox")
            .numbox()
            .setOption("max", newVal);
        }
      }
    
    

    vuex 优化购物车

    加入购物车按钮:

        addToShopCar() {
          // 添加到购物车
          this.ballFlag = !this.ballFlag;
          // { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false  }
          // 拼接出一个,要保存到 store 中 car 数组里的 商品信息对象
          var goodsinfo = {
            id: this.id,
            count: this.selectedCount,
            price: this.goodsinfo.sell_price,
            selected: true
          };
          // 调用 store 中的 mutations 来将商品加入购物车
          this.$store.commit("addToCar", goodsinfo);
        }
    
    

    购物车vuex里的store:

    var car = JSON.parse(localStorage.getItem('car') || '[]')
    
    var store = new Vuex.Store({
      state: { // this.$store.state.***
        car: car // 将 购物车中的商品的数据,用一个数组存储起来,在 car 数组中,存储一些商品的对象, 咱们可以暂时将这个商品对象,设计成这个样子   
        // { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false  }
      },
      mutations: { // this.$store.commit('方法的名称', '按需传递唯一的参数')
        addToCar(state, goodsinfo) {
          // 点击加入购物车,把商品信息,保存到 store 中的 car 上
          // 分析:
          // 1. 如果购物车中,之前就已经有这个对应的商品了,那么,只需要更新数量
          // 2. 如果没有,则直接把 商品数据,push 到 car 中即可
    
          // 假设 在购物车中,没有找到对应的商品
          var flag = false
    
          state.car.some(item => {
            if (item.id == goodsinfo.id) {
              item.count += parseInt(goodsinfo.count)
              flag = true
              return true
            }
          })
    
          // 如果最终,循环完毕,得到的 flag 还是 false,则把商品数据直接 push 到 购物车中
          if (!flag) {
            state.car.push(goodsinfo)
          }
    
          // 当 更新 car 之后,把 car 数组,存储到 本地的 localStorage 中
          localStorage.setItem('car', JSON.stringify(state.car))
        },
        updateGoodsInfo(state, goodsinfo) {
          // 修改购物车中商品的数量值
          // 分析: 
          state.car.some(item => {
            if (item.id == goodsinfo.id) {
              item.count = parseInt(goodsinfo.count)
              return true
            }
          })
          // 当修改完商品的数量,把最新的购物车数据,保存到 本地存储中
          localStorage.setItem('car', JSON.stringify(state.car))
        },
        removeFormCar(state, id) {
          // 根据Id,从store 中的购物车中删除对应的那条商品数据
          state.car.some((item, i) => {
            if (item.id == id) {
              state.car.splice(i, 1)
              return true;
            }
          })
          // 将删除完毕后的,最新的购物车数据,同步到 本地存储中
          localStorage.setItem('car', JSON.stringify(state.car))
        },
        updateGoodsSelected(state, info) {
          state.car.some(item => {
            if (item.id == info.id) {
              item.selected = info.selected
            }
          })
          // 把最新的 所有购物车商品的状态保存到 store 中去
          localStorage.setItem('car', JSON.stringify(state.car))
        }
      },
      getters: { // this.$store.getters.***
        // 相当于 计算属性,也相当于 filters
        getAllCount(state) {
          var c = 0;
          state.car.forEach(item => {
            c += item.count
          })
          return c
        },
        getGoodsCount(state) {
          var o = {}
          state.car.forEach(item => {
            o[item.id] = item.count
          })
          return o
        },
        getGoodsSelected(state) {
          var o = {}
          state.car.forEach(item => {
            o[item.id] = item.selected
          })
          return o
        },
        getGoodsCountAndAmount(state) {
          var o = {
            count: 0, // 勾选的数量
            amount: 0 // 勾选的总价
          }
          state.car.forEach(item => {
            if (item.selected) {
              o.count += item.count
              o.amount += item.price * item.count
            }
          })
          return o
        }
      }
    })
    
    

    vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。公用数据存储处理模块

    1. 使用步骤1:导包
    import Vuex from 'vuex'
    // 3. 注册vuex到vue中
    Vue.use(Vuex)
    
    
    1. 新建store实例
    var store = new Vuex.Store({
      state: {
        // 大家可以把 state 想象成 组件中的 data ,专门用来存储数据的
        // 如果在 组件中,想要访问,store 中的数据,只能通过 this.$store.state.*** 来访问
        count: 0
      },
      mutations: {
        // 注意: 如果要操作 store 中的 state 值,只能通过 调用 mutations 提供的方法,才能操作对应的数据,不推荐直接操作 state 中的数据,因为 万一导致了数据的紊乱,不能快速定位到错误的原因,因为,每个组件都可能有操作数据的方法;
        increment(state) {
          state.count++
        },
        // 注意: 如果组件想要调用 mutations 中的方法,只能使用 this.$store.commit('方法名')
        // 这种 调用 mutations 方法的格式,和 this.$emit('父组件中方法名')
        subtract(state, obj) {
          // 注意: mutations 的 函数参数列表中,最多支持两个参数,其中,参数1: 是 state 状态; 参数2: 通过 commit 提交过来的参数;
          console.log(obj)
          state.count -= (obj.c + obj.d)
        }
      },
      getters: {
        // 注意:这里的 getters, 只负责 对外提供数据,不负责 修改数据,如果想要修改 state 中的数据,请 去找 mutations
        optCount: function (state) {
          return '当前最新的count值是:' + state.count
        }
        // 经过咱们回顾对比,发现 getters 中的方法, 和组件中的过滤器比较类似,因为 过滤器和 getters 都没有修改原数据, 都是把原数据做了一层包装,提供给了 调用者;
        // 其次, getters 也和 computed 比较像, 只要 state 中的数据发生变化了,那么,如果 getters 正好也引用了这个数据,那么 就会立即触发 getters 的重新求值;
      }
    })
    
    

    state

    • *mapState 辅助函数
    1. 某个组件想要用到store里state里的数据时,
    // 在单独构建的版本中辅助函数为 Vuex.mapState
    import { mapState } from 'vuex'
    
    export default {
      // ...
      computed: mapState({
        // 箭头函数可使代码更简练
        count: state => state.count,
    
        // 传字符串参数 'count' 等同于 `state => state.count`
        countAlias: 'count',
    
        // 为了能够使用 `this` 获取局部状态,必须使用常规函数
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
      })
    }
    
    

    ​ 2. 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。

    computed: mapState(['count'])
    // 映射 this.count 为 store.state.count
    
    
    1. 当与局部计算属性混合使用:
    computed: {
      localComputed () { /* ... */ },
      // 使用对象展开运算符将此对象混入到外部对象中
      ...mapState({
        // ...
      })
    }
    
    

    getters

    Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

    const store = new Vuex.Store({
      state: {
        todos: [
          { id: 1, text: '...', done: true },
          { id: 2, text: '...', done: false }
        ]
      },
      getters: {
        doneTodos: state => {
          return state.todos.filter(todo => todo.done)
        }
      }
    })
    
    

    通过属性访问:

    store.getters.doneTodos 
    
    

    Getter 也可以接受其他 getter 作为第二个参数:

    getters: {
      // ...
      doneTodosCount: (state, getters) => {
        return getters.doneTodos.length
      }
    }
    
    

    你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。

    getters: {
      // ...
      getTodoById: (state) => (id) => {
        return state.todos.find(todo => todo.id === id)
      }
    }
    
    
    store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
    
    

    mapGetters辅助函数

    return方法的gettersb不能用mapGetters映射

    export default {
      // ...
      computed: {
      // 使用对象展开运算符将 getter 混入 computed 对象中
        ...mapGetters([
          'doneTodosCount',
          'anotherGetter',
          // ...
        ]),
        getTodoById: (state) => (id) => {
        return state.todos.find(todo => todo.id === id)
      }   
      }
    }
    
    

    mutation

    • 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    
    

    需要以相应的 type 调用 store.commit 方法:

    store.commit('increment')
    
    
    • 提交载荷(Payload)

    你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)

    // ...
    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    
    
    store.commit('increment', 10)
    
    

    在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

    // ...
    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
    
    
    • 对象风格的提交方式
    store.commit({
      type: 'increment',
      amount: 10
    })
    
    
    • Mutation 必须是同步函数

    • mapMutations

    export default {
      // ...
      methods: {
        ...mapMutations([
          'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    
          // `mapMutations` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
        ]),
        ...mapMutations({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
        })
      }
    }
    
    

    Action

    Action 类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作。
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        incrementByMutation (state,playLoad) {
          state.count + = playload.count
        }
      },
      actions: {
        incrementByAction (context) {
          context.commit('incrementByMutation')
        }
      }
    })
    
    

    Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。

    实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

    actions: {
      incrementByAction ({ commit }) {
        commit('incrementByMutation')
      }
    }
    
    

    Action 通过 store.dispatch 方法触发:

    store.dispatch('incrementByAction')
    
    

    可以在 action 内部执行异步操作:

    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() => {
          commit('incrementByMutation')
        }, 1000)
      }
    }
    
    

    Actions 支持同样的载荷方式和对象方式进行分发:

    // 以载荷形式分发
    store.dispatch('incrementAsync', {
      amount: 10
    })
    
    actions: {
      incrementAsync ({ commit },count) {
        setTimeout(() => {
          commit('incrementByMutation',count)
        }, 1000)
      }
    }
    
    
    

    组合action:

    actions: {
      actionA ({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit('someMutation')
            resolve()
          }, 1000)
        })
      }
    }
    
    store.dispatch('actionA').then(() => {
      // ...
    })
    
    
    actions: {
      // ...
      actionB ({ dispatch, commit }) {
        return dispatch('actionA').then(() => {
          commit('someOtherMutation')
        })
      }
    }
    
    

    module

    使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

    Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

    const moduleA = {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: { ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    
    
    • 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
    const moduleA = {
      state: { count: 0 },
      mutations: {
        increment (state) {
          // 这里的 `state` 对象是模块的局部状态
          state.count++
        }
      },
    
      getters: {
        doubleCount (state) {
          return state.count * 2
        }
      }
    }
    
    
    • 同样,对于模块内部的 action,局部状态通过 context.state暴露出来,根节点状态则为 context.rootState:
    const moduleA = {
      // ...
      actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
          if ((state.count + rootState.count) % 2 === 1) {
            commit('increment')
          }
        }
      }
    }
    
    
    • 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
    const moduleA = {
      // ...
      getters: {
        sumWithRootCount (state, getters, rootState) {
          return state.count + rootState.count
        }
      }
    }
    
    

    无命名空间情况下,模块内部的 getter、mutation 和 getter 是注册在全局命名空间下的,所以:

    1. 访问a模块下count的state:
    store.state.a.count
    
    
    1. 访问a模块下的mutation ,getter,action
    store.commit('someMutationInA')
    
    
    store.getters.someGetterINA
    
    
    store.dispatch('someActionINA')
    
    

    添加命名空间:

    • 访问a模块下的mutation ,getter,action:
    store.commit('a/someMutationInA')
    
    
    store.getters['a/someGetterINA']
    
    
    store.dispatch('a/someActionINA')
    
    
    • 在带命名空间的模块内访问全局内容:
    modules: {
      foo: {
        namespaced: true,
    
        getters: {
          // 在这个模块的 getter 中,`getters` 被局部化了
          // 你可以使用 getter 的第四个参数来调用 `rootGetters`
          someGetter (state, getters, rootState, rootGetters) {
            getters.someOtherGetter // -> 'foo/someOtherGetter'
            rootGetters.someOtherGetter // -> 'someOtherGetter'
          },
          someOtherGetter: state => { ... }
        },
    
        actions: {
          // 在这个模块中, dispatch 和 commit 也被局部化了
          // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
          someAction ({ dispatch, commit, getters, rootGetters }) {
            getters.someGetter // -> 'foo/someGetter'
            rootGetters.someGetter // -> 'someGetter'
    
            dispatch('someOtherAction') // -> 'foo/someOtherAction'
            dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
    
            commit('someMutation') // -> 'foo/someMutation'
            commit('someMutation', null, { root: true }) // -> 'someMutation'
          },
          someOtherAction (ctx, payload) { ... }
        }
      }
    }
    
    
    • 带命名空间的模块注册全局 action
    {
      actions: {
        someOtherAction ({dispatch}) {
          dispatch('someAction')
        }
      },
      modules: {
        foo: {
          namespaced: true,
    
          actions: {
            someAction: {
              root: true,
              handler (namespacedContext, payload) { ... } // -> 'someAction'
            }
          }
        }
      }
    }
    
    
    • 带命名空间的绑定函数
    computed: {
      ...mapState({
        a: state => state.some.nested.module.a,
        b: state => state.some.nested.module.b
      })
    },
    methods: {
      ...mapActions([
        'some/nested/module/foo', // -> this['some/nested/module/foo']()
        'some/nested/module/bar' // -> this['some/nested/module/bar']()
      ])
    }
    
    

    简化:

    computed: {
      ...mapState('some/nested/module', {
        a: state => state.a,
        b: state => state.b
      })
    },
    methods: {
      ...mapActions('some/nested/module', [
        'foo', // -> this.foo()
        'bar' // -> this.bar()
      ])
    }
    
    

    而且,你可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:

    import { createNamespacedHelpers } from 'vuex'
    
    const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
    
    export default {
      computed: {
        // 在 `some/nested/module` 中查找
        ...mapState({
          a: state => state.a,
          b: state => state.b
        })
      },
      methods: {
        // 在 `some/nested/module` 中查找
        ...mapActions([
          'foo',
          'bar'
        ])
      }
    }
    
    

    你可能感兴趣的:(Vue笔记(有点乱))