Vue学习记录

[toc]

邂逅Vue.js

1.编程范式
- 命令式编程 如jquery
- 声明式编程 只需要生命需要显示的东西就可以

  1. 创建Vue实例的时候,传入了一个对象options,这个opentions中包含了
    - el: 类型:string|HTMLElement 作用:决定之后Vue实例会管理哪一个DOM
    - data: 类型:Object|Function(组件中data必须是一个函数) 作用:Vue实例对应的数据对象
    - methods: 类型:{[key: string] : Function} 作用: 定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。

Vue基础语法

  1. 前端代码缩进最好两个空格,多数大型开源框架都是两个

模板语法

Mustache语法({{}})

  1. 可以写变量,也可以写简单的表达式
    {{message}}
    {{firstName + ' ' + lastName}}
    

指令

  1. v-once
    - 该指令后面不需要跟任何表达式
    - 该指令表示元素和组件只渲染一次,不会随数据的改变而改变
    <h2 v-once>{{message}}<h2>
    
  2. v-html
    - 该指令后面往往会跟上一个string类型
    - 会将string的html解析出来并且进行渲染
    <h2 v-html='url'><h2>
    data:{
        url: '<a href="http://www.baidu.com">百度一下<a>'
    }
    
  3. v-text
    {{message}}
    <h2 v-text='message'><h2>
    
    一般不用,不够灵活,会覆盖innerHTML内容
  4. v-pre , 原生显示
    <h2 v-pre>{{message}}</h2>
    
  5. v-cloak
    - 在vue解析之前,div中有一个属性v-cloak
    - 在vue解析之后,div中的v-cloak属性被删除
    - 主要是用来防止界面上显示未渲染的代码
     <div  id='app'>{{message}}<div>
     <script>
     new Vue({
        el: '#app',
        data{
            message: 'aaa'
        }
     })
     </script>
     <style>
        [v-cloak] {
            display: none
        }
     </style>
    
  6. v-bind
    - 动态绑定属性
    - 缩写: ':'
    - 预期: any(with argument) | Object(without argument)
    - 参数: attrOrProp(optional)
    <img  v-bind:src='imgUrl'>
    语法糖写法
    <img  :src='imgUrl'>
    <h1 :class = '{class1(类名): showClass1(bool值), class2: showClass2}'><h1>
    <h1 :class = 'getClass()'><h1>
    methods:{
     getClass: function(){
        return {class1: showClass1, class2: showClass2}
     }
    }
    
  7. v-bind:style
    - 动态绑定样式
    - <h2 :style="{fontSize: '50px'}"></h2>
    - <h2 style="{fontSize: finalSize + 'px'}"></h2>

计算属性

  1. 数据展示前需要进行处理时使用。
    computed:{
        fullName: function(){
            return this.firstName + this.lastName
            }
    }
    
  2. 计算属性会缓存,多次调用只会计算一次,方法不会缓存
  3. 计算属性的getter和setter(set不常用)
    computed: {
        fullName: {
            get: function(){
                return this.firstName + ' ' + this.lastName
            },
            set: function(name){
                this.fullName = name
            }
        }
    }
    

对象字面量增强写法

 // 普通写法
 name = 'aaa'
 age = 18
const obj = {
    name: name,
    age: age
}
// 增强写法
const obj = {
    name,
    age
}

事件监听

  1. v-on
    - 作用: 绑定事件监听器
    - 缩写: @
    - 预期: Function| Inline Statement | Object
    - 参数: event
  2. 参数传递
    - 如果方法不需要额外参数,那么方法后的()可以不添加
    - 如果方法本身有参数,但是调用的时候没有传递,省略小括号,默认会将event参数传递进去
    - 如果同时需要传入某个参数,同时需要event,可以通过$event传入事件
    <button @click="btnClick"></button>    默认传过去event
    <button @click="btnClick()"></button>   默认传过去undefined
    <button @click="btn1Click('aaa',$event)"></button>
    methods:{
        btnClick(aaa){
            console.log(aaa)
        },
        btn1Click(aaa, event){
            console.log(aaa)
        }
    }
    
  3. v-on修饰符的使用
    - .stop 阻止冒泡
      <div @click='divClick'>
        <button @click.stop="btnClick"></button>
     </div>
    
        - prevent   阻止默认事件
    
    <form action="baidu" @click.prevent="submitClick">
    </form>
    # 阻止默认提交事件
    
      - .{keyCode | keyAlias}   只当事件是从特定键触发时才触发回调
      - .native   监听根元素的原生事件
      - .once  只触发一次回调
    

条件判断

  1. v-if
  2. v-else-if
  3. v-else
  4. vue内部会复用dom元素,如果不想让复用,可以加个key
  5. v-show
    - v-show dom会渲染,使用样式决定是否显示
    - v-if 根据条件判断是否要渲染
    - 频繁切换使用v-show

循环

  1. v-for遍历
    官方推荐在使用v-for遍历时,给对应的元素或组件加上一个:key属性
    为什么要使用key属性?
    假设有一个数组,现在想在数组中间插入一个元素,如果没有key,则插入位置以及后面的位置都需要改变,而如果有key,则会先使用key与元素对应起来,插入时只修改插入位置即可。key必须与item一一对应,所以不能用index。


    遍历时key的作用
  • 使用key给每一个节点做一个唯一标识
  • Diff算法可以正确的识别此节点
  • 找到正确的位置插入新的节点
    ==key的作用主要是为了更高效的更新虚拟DOM==
  1. 哪些数组的方法是响应式的?
    - push(): 可以添加多个 arr.push('aaa', 'bbb', 'ccc')
    - pop(): 删除数组的最后一个元素
    - shift() : 删除数组第一个元素
    - unshift(): 在数组前面添加元素, 可以添加多个
    - sort():
    - reverse()
    - splice() : 删除元素/插入元素/替换元素 会影响原始数组
    第一个参数start,表示开始操作的索引
    删除元素: 第二个参数表示要删除几个元素,如果没有传,表示删除所有
    替换元素: 第二个参数表示要替换的个数,后面参数表示要替换的元素
    插入元素: 第二个参数为0,后面参数为添加的元素
    arr = [1,2,3,4,5]
    arr.splice(1)  => [2,3,4,5]
    arr = [1,2,3,4,5]
    arr.splice(1,2)⇒ [2,3]
    arr = [1,2,3,4,5]
    arr.splice(1,2,'m','n', 'o') => [1, "m", "n", "o", 4, 5]
    arr = [1,2,3,4,5]
    arr.splice(1,0,'m', 'n')==> [1, "m", "n", 2, 3, 4, 5]
    

==通过数组索引直接修改元素不是响应式的, 直接修改界面不会更新==
- Vue.set(obj, index, newValue)
Vue.set(arr, 0, ''a)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2 v-show="books.length==0">购物车为空</h2>
    <table v-show="books.length!=0">
      <tr>
        <th>书籍名称</th>
        <th>出版日期</th>
        <th>价格</th>
        <th>购买数量</th>
        <th>操作</th>
      </tr>
      <tr v-for="(book, index) in books" :key='book.bookName'>
        <td>{{book.bookName}}</td>
        <td>{{book.publishDate}}</td>
        <td>{{book.price | transPrice}} </td>
        <td><button @click="increment(index)">+</button>{{book.number}}<button @click="decrement(index)">-</button></td>
        <td><button @click="remove(index)">移除</button></td>
      </tr>
    </table>
    <p>总价:¥{{totalPrice | transPrice}}</p>
  </div>
</body>
</html>


<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>


<script>
  const app = new Vue({
    el: '#app',
    data: {
      books: [
        {'bookName': '《算法导论》', 'publishDate': '2006-9', 'price': 85.00, 'number': 0},
        {'bookName': '《UNIX变成艺术》', 'publishDate': '2006-2', 'price': 59.00, 'number': 0},
        {'bookName': '《编程珠玑》', 'publishDate': '2008-10', 'price': 39.00, 'number': 0},
        {'bookName': '《代码大全》', 'publishDate': '2006-12', 'price': 128.00, 'number': 0}
      ],
    },
    computed: {
      totalPrice(){
        let result = this.books.reduce(function(preValue, book){
          return preValue + book.price * book.number
        }, 0)
        return result;
      }
    },
    methods:{
      increment(index, book){
        this.books[index].number--;
      },
      decrement(index, book){
        this.books[index].number--;
      },
      remove(index){
        this.books.splice(index,1);
      }
    },
    filters: {
      transPrice(price){
        if(typeof(price)=='number'){
          return price.toFixed(2);
        }
      }
    }
  })
</script>
  1. 高阶函数
    - filter() : filter回调函数必须返回一个bool值
    - map()
    - reduce(): 对数组中所有内容进行汇总
    arr = [1,2,3,4]
    arr.reduce(function(preValue, n){
        return preValue + n;
    }, 0)
    0 表示preValue的初始值, n表示每次遍历时的元素
    
  2. 表单绑定v-model
    - v-model其实是一个语法糖,他的背后本质包含两个操作
    - v-bind绑定一个value属性
    - v-on指令给当前元素绑定input事件
    <input type='text' v-model='message'>
    相当于
    <input type='text', :value='message' @input='message= $event.target.value'>
    相当于
    <input type='text' value='message' @input='changeMessage'>
    methods:{
        changeMessage(event){
            this.message = event.target.value;
        }
    }
    
  3. v-model结合radio
      <label for="male">
        <input type="radio" id="male" value="男" v-model='sex'>男
      </label>
      <label for="female">
        <input type="radio" id="female" value="女" v-model='sex'>女
      </label>
      data{
        sex: '男'
      }
    
  4. v-model结合checkbox
          <!-- 单选 -->
    <label for="license">
    <input type="checkbox" id="license" v-model='isAgree'>同意协议
    </label>
    <button :disabled='!isAgree'>下一步</button>
    data{
        isAgree: false
    }
    <div>
    <!-- 多选 -->
    <input type="checkbox" value="篮球" v-model="hobbies">篮球
    <input type="checkbox" value="足球" v-model="hobbies">足球
    <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
    <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
    </div>
    data{
        hobbies: []
    }
    
  5. v-select :单选(字符串) 多选(数组)
  6. v-model 修饰符
    - .lazy <input type="text" v-model.lazy="message"> 敲回车或失去焦点才同步数据
    - .number <input type="number" v=model.number="age">
    - .trim <input type="text" v-model.trim="message">

组件化开发

组件注册步骤

  1. 创建组件构造器对象
  2. 注册组件
  3. 使用组件
    const cpn = Vue.extend({
        template: `<div><h2>hhhhh</h2></div>`
    })
    Vue.components('my-cpn', cpn)
    
  4. 全局组件和局部组件
    - 全局组件可以在多个Vue实例中使用
    - 局部组件
    const app = new Vue({
        el: '#app',
        data:{}
        conponents: {
            cpn: cpn     #局部组件
        }
    }
    
  5. 父组件和子组件
  6. 注册组件语法糖, 主要时省去了调用Vue.extend()的步骤,而是直接可以使用一个对象来代替
    // 全局
    Vue.components('cpn', {
        template: `<div><h2>hhhhh</h2></div>`
    })
    

组件间的通信

父子组件的通信

  1. 通过props向子组件传递数据
<div id="app">
    <cpn :cmovies="movies"></cpn>
</div>

<template id="cpn">
    <div>
        <ul>
            <li v-for="movie of cmovies" :key="movie">{{movie}}</li>
        </ul>
    </div>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            movies: ['aaa', 'bbb', 'ccc'],
            message: 'hello world'
        },
        components: {
            'cpn': {
                template: '#cpn',
                data(){
                    return{

                    }
                },
                // props: ['cmovies']
                // props:{
                //     cmovies: Array
                // }
                props:{
                    cmovies: {
                        type: Array,
                        default(){
                          return [1,2,3]
                         }
                        required: true
                    }
                }
            }
        }
    })
</script>

==props里面如果是驼峰式写法,在v-bind用的时候要使用-连接==
cMovies ---> :c-movies="movies"

  1. 通过事件向父组件发送消息
    • 在子组件中,通过$emit()来触发事件
    • 在父组件中,通过v-on来监听子组件事件
<div id="app">
    <cpn :cmovies="movies" @item-click="cpnClick"></cpn>
</div>

<template id="cpn">
    <div>
        <button v-for="movie of cmovies" :key="movie" @click="itemClick(movie)">{{movie}}</button>
    </div>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            movies: ['aaa', 'bbb', 'ccc'],
            message: 'hello world'
        },
        methods:{
            cpnClick(item){
                console.log(item);
            }
        },
        components: {
            'cpn': {
                template: '#cpn',
                data(){
                    return{

                    }
                },
                methods: {
                    itemClick(item){
                        this.$emit('item-click', item)
                    }
                }
            }
        }
    })

</script>

父子组件的访问方式

  1. 父组件访问子组件:使用$children或$refs
    this.$children 是一个数组,包含了所有的子组件
    <child  refs='aaa'></child>
    在父组件中使用this.$refs.aaa获取对应子组件
    
  2. 子组件访问父组件:使用$parent
  3. 访问根组件$root

插槽(slot)

  • 组件的插槽是为了让封装的组件更加有扩展性
  • 让使用者可以决定组件内部的一些内容到底展示什么
  1. 插槽的基本使用
    <slot></slot>
  2. 插槽的默认值
    <slot><button>按钮</button></slot>
  3. 如果有多个值,同时放到组件中替换,一起作为替换元素

具名插槽

<cpn>
    <button slot='left'><button>
<cpn>

<template id='cpn'>
    <slot name='left'></slot>
    <slot name='center'></slot>
    <slot name='right'></slot>
</template>

作用域插槽

父组件替换插槽的标签,但是内容由子组件来提供

<div id="app">
    <cpn>
        <template slot-scope='slot'>
            <span>{{slot.data.join('-')}}</span>
        </template>
    </cpn>
    <cpn>
        <template slot-scope='slot'>
            <span>{{slot.data.join('*')}}</span>
        </template>
    </cpn>
</div>

<template id="cpn">
    <div>
        <slot :data='languages'>
            <ul>
                <li v-for="item of languages">{{item}}</li>
            </ul>
        </slot>
    </div>
</template>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
    var app = new Vue({
        el: '#app',
        data(){
            return {

            }
        },
        components: {
            cpn: {
                template: '#cpn',
                data(){
                    return {
                        languages: ['aaa', 'bbb', 'ccc', 'ddd']
                    }
                }
            }
        }
    })
</script>

模块化开发

模块化有两个核心:导出和导入
CommonJs的导出

moduleA.exports = {
     flag: true,
     sum(a,b){
        return a + b
     }
}

CommonJS的导入

let {flag, sum} = require(‘moduleA’)

webpack入门

webpack使用

Vue Cli详解

安装

npm install -g @vue/cli

初始化项目

  • Vue cli3 vue create projectName
  • 兼容Vue cli2
    npm install @vue/cli-init -g
    vue init webpack projectName

Vue CLI2使用

vue init webpack vuecli2test
输入上述命令后有以下配置:

  • Project name : 项目名称,不能包含大写
  • Project description
  • Author: 作者信息,默认会从git中读取
  • Vue build : runtime + complier和runtime-only
  • Install vue-router : 是否安装路由
  • 其他不重要的
    chrome使用v8引擎直接将js代码转换成二进制文件,所以运行速度较快
    node环境可以直接执行js文件
    node test.js
    static文件夹和assets文件夹的区别?
    都是存放静态文件的,assets中的图片会根据设置的limits,将大于的使用hash重命名后放到dist下对应文件夹中,小于的转换成base64格式直接显示。而static中的文件,webpack不会进行处理,会原样显示。

runtime+complier和runtime-only的区别

image-20201128195831819.png
//runtime+complier
import App from './App'
new Vue({
    el: '#app',
    template: '<App/>'
})
template-->ast(抽象语法树)-->render-->virtualDom-->realDom
//runtime-only (性能更高,代码更少(少了complier那部分))
new Vue({
    el: '#app',
    render: h=>h(App)
})
h实际是createElement函数
普通用法: createElement(标签, {属性}, [内容])
createElement('h2', {class: 'box', ['hello world']})
也可以直接传入组件: createElement(App)
render-->virtualDom-->realDom
runtime-only模式不包含template,那么app中的template是如何处理的?使用vue-template-complier将template编译成render函数后才使用
vue build过程
image-20201128211644528.png

Vue Cli3

vue-cli3和vue-cli2的区别

  • vue-cli3是基于webpack4打造,vue-cli2还是webpack3
  • vue-cli3的设计原则是“0配置”,移除了配置文件目录下的build和config等目录
  • vue-cli3提供vue ui命令,提供了可视化配置,更加人性化
  • 移除了static文件夹,新增了public文件夹,并且index.html移动到public中

Vue Cli3修改配置的方法

  1. vue ui启动一个本地服务,在界面上修改

  2. node_modules下面的@vue/webpack.config.js

  3. 在当前项目下创建一个文件,vue.config.js

    module.exports = {
    }
    

vue-router

Vuex详解

网络封装

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • slot 如果在自定义标签中直接写入内容,内容会被替换掉,为了避免这个问题,Vue提供了slot插槽标签 默认值 ...
    大白熊_8133阅读 590评论 0 0
  • 复习 let vm=new Vue()vm=viewModel 数据最终都会被vm代理 {{msg}} 表达式,赋...
    大白熊_8133阅读 526评论 0 0
  • 钩子函数的生命周期 根实例,在初始化时会调用很多方法,这些方法被称为钩子函数生命周期:beforeCreate c...
    大白熊_8133阅读 637评论 0 0
  • vue data 组件定义只接受 function 父子组件属性传递: 父组件的子组件中 v-bind :prop...
    斐硕人阅读 256评论 0 0
  • 没全部看完,我是菜鸡 动态绑定样式 和class方法相同,但是需要使用驼峰命名 对象方法 数组 路由 实现单页开发...
    大白熊_8133阅读 569评论 0 0