【美团网项目】2.Vue基础知识

知识点

  • 环境搭建(如何快速搭建 Vue 环境 / 创建一个 Vue 项目)
  • 模板语法
  • 计算属性
  • 类与样式
  • 条件&列表渲染
  • 事件处理
  • 深入了解组件
  • 路由基础(单页面应用会涉及到路由,如何配置路由)
  • Vuex基础(跨组件通信、状态保护都依赖于 Vuex)

环境搭建

搭建环境有很多种方式,这里我们采用最快速最便捷的脚手架(构建工具 vue-cli)方式,这个脚手架是 vue 官方提供的。

  • vue-cli@3(vue 脚手架,当前版本是3,是 vue 的一个构建工具)
  • vue@2.5(vue 语法,当前使用的是2.5版本)

vue 脚手架指的是 vue-cli,它是一个专门为单页面应用快速搭建繁杂的脚手架,它可以轻松的创建新的应用程序而且可用于自动生成 vue 和 webpack 的项目模板。

1. 安装 vue-cli 构建工具(-g 表示全局安装)

$ npm install -g @vue/cli
# 或
$ yarn global add @vue/cli

# 查看安装的版本
$ vue -V

2. 创建一个 Vue 项目:使用 vue-cli 脚手架快速创建

# 基于交互式命令行的方式创建新版 vue 项目
$ vue create vue-demo
# 或
# 基于图形化界面的方式创建新版 vue 项目
$ vue ui

【补充说明】:使用 npm install -g @vue/cli 命令进行安装,如果在Mac上报如下错误:

# MacOS,安装npm全局包提示没有写入权限:
npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules

解决方法:
修改npm包所安装目录的权限:sudo chown -R $USER /usr/local 然后输入密码就可以了
查看目录是否已切换权限:ls -l /usr/local

3. 编译、运行 vue 项目

# 进入项目目录
$ cd vue-demo
# 编译项目
$ npm run serve
  • 编译成功如下:
 DONE  Compiled successfully in 2878ms                         4:00:54 ├F10: PM┤

  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.1.121:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

复制上面的网址到浏览器即可预览项目运行的效果~

模板语法

  • 插值(文本赋值)
<!-- 1.【纯文本赋值】插值表达式 -->
<span>{{ msg }}</span>
<!-- 2.【纯文本赋值】使用 v-text 指令 -->
<span v-text="msg"></span>
<!-- 3.【一次性赋值】当数据改变时,插值处的内容不会更新 -->
<span v-once>{{ msg }}</span>
<!-- 4.【富文本赋值】使用 v-html 指令:可以输出带 HTML 标签的文本 -->
<span v-html="msg">内容</span>

关于v-html:你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

  • 绑定属性指令 v-bind:属性名
<input type="button" value="按钮" v-bind:title="mytitle">
<!-- "v-bind:" 可以缩写成 ":" -->
<input type="button" value="按钮" :title="mytitle">
  • 绑定事件指令 v-on:事件名
<input type="button" value="按钮" v-on:click="show">
<!-- "v-on:" 可以缩写成 "@" -->
<input type="button" value="按钮" @click="show">

计算属性

  • 对于任何复杂逻辑,你都应当使用计算属性,可以解决响应式依赖问题。
  • 应用场景:具有依赖关系的数据监听。
<div class="demo1">
  <p>{{ message }}</p>
  <!-- message 发生改变时,reversedMessage会更新 -->
  <p>翻转字符串: "{{ reversedMessage }}"</p>
</div>

<div class="demo2">{{ fullName }}</div>
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello',
    firstName: 'Foo',
    lastName: 'Bar'
  },
  // 计算属性
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    },
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

类与样式

  • 动态切换样式
<!-- 一般用法 -->
<h1 class="red thin">我是一个很长很长的标题!!!</h1>

<!-- Vue中用法:使用数组 -->
<h1 v-bind:class="['red', 'thin']">我是一个很长很长的标题!!!</h1>
<h1 :class="['red', 'thin', isActive ? 'active' : '']">我是一个很长很长的标题!!!</h1>
<h1 :class="['red', 'thin', {'active': isActive}]">我是一个很长很长的标题!!!</h1>

<!-- Vue中用法:直接使用对象 -->
<h1 :class="{red: true, thin: true, active: isActive}">我是一个很长很长的标题!!!</h1>

<!-- 绑定一个返回对象的计算属性。这是一个常用且强大的模式 -->
<h1 :class="classObject">我是一个很长很长的标题!!!</h1>
data: {
  isActive: true,
  error: null
},
computed: {
  // 计算属性:当 isActive 或者 hasError 变化时,class 列表将相应地更新。
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

条件&列表渲染

  • 条件渲染 v-if
<!-- v-if 的特点:每次都会重新删除或创建元素 -->
<!-- v-show 的特点:每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式 -->
<h1 v-if="flag">这是用v-if控制的元素</h1>
<h1 v-show="flag">这是用v-show控制的元素</h1>
  • 列表渲染 v-for
<!-- 遍历数组:list 是数据源数据,item 是数组的每一项(item也可以用其它名称,如:obj),key 跟踪每个节点的唯一性 -->
<p v-for="item in list" :key="item.id">{{ item }}</p>
<p v-for="(item, i) in list" :key="item.id">索引值:{{ i }} , 每一项的值:{{ item.name }}</p>

事件处理

<!-- v-on 指令:后面可以直接跟JS表达式 -->
<button v-on:click="counter += 1">Add 1</button>
<!-- v-on 指令:后面可以跟方法名 -->
<button v-on:click="show"></button>
<!-- v-on 指令:后面可以跟方法名 + 传参 -->
<button v-on:click="show('hello')"></button>

<!-- $event:传入原始的 DOM 事件,即原生事件对象-->
<button v-on:click="show('hello', $event)"></button>

<!-- v-on: 可以缩写成 @,后面可以加【事件修饰符】(.stop 阻止事件冒泡) -->
<button @click.stop="show('hello')"></button>
<!-- 监听“Enter键”抬起事件(即输入框键盘回车事件)【按键修饰符】 -->
<input type="text" v-model="name" @keyup.enter="nameEnter">

深入了解组件

组件:模板(必须的,且仅有一个根节点) + script + 样式(如:HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div v-html="msg1"></div>
  </div>
</template>

<script>

export default {
  /*
    name 相当于一个全局 ID;
    name 非必选项,可以不写;
    写了可以提供更好的调试信息(官方文档有)。
   */
  name: "HelloWorld",

  // 组件传参入口,接收外部传入的参数
  props: {
    msg: String,
  },

  /*
    data: {
      msg1: "aaa"
    }
  */

  // 组件中的data属性必须是函数,这里不能使用data对象。原因是这里有多实例共享data的问题,使用函数让每个组件实例各自维护自己的data(独立的拷贝),互不影响。
  data() {
    return {
      msg1: '<span style="color:red">子组件富文本</span>',
    }
  },

  methods: {
    show() {
      console.log('show方法');
    }
  }
};
</script>

<!-- 私有样式:添加“scoped”属性来限制CSS只适用于此组件内部-->
<style scoped>
h1 {
  margin: 40px 0 0;
}
</style>
  • props:组件之间的参数传递(父组件向子组件传参,单向的)
<script>
export default {
  name: 'HelloWorld',

  // 组件传参入口,接收外部(父组件)传入的参数
  // props: ['age'],
  props: {
    age: Number
  },

  data() {
    return {
      name: '张三'
    }
  }
}
</script>
  • slot:插槽在组件抽象设计中的应用。我们在定义组件时,可以在组件中放一个插槽,插槽在将来可以放任意的不确定的子组件(即可以通过插槽动态往组件中添加内容),从而提高组件的灵活性和复用性。
<!-- 1.定义组件-->
<template>
  <div class="hello">
    <!-- 在头部添加插槽 -->
    <slot name='header'></slot>
    <h1>子组件主体内容</h1>
    <!-- 在尾部添加插槽 -->
    <slot name='footer'></slot>
  </div>
</template>

... ...

<!-- 2.使用组件-->
<template>
  <div id="app">
    <HelloWorld :age='myAge'>
      <h3 slot='header'>自定义头部内容</h3>
      <h3 slot='footer'>自定义尾部内容</h3>
    </HelloWorld>
  </div>
</template>
  • 自定义事件:父子组件的通信方式(可以解决子组件向父组件传递参数)
<!-- 自定义事件1:组件内部的事件定义 -->
<!-- <button type='button' name='button' @click='handleMsg'>发送到父组件</button> -->
<!-- 【子组件中】自定义事件2:组件外部的事件定义(子组件将消息发送到父组件,可以携带参数) -->
<button type='button' name='button' @click='$emit("handleMsg", 36)'>发送到父组件</button>

<!-- 【父组件中】通过自定义事件(@handleMsg),接收子组件发送的消息 -->
<!-- 在这里可以通过 $event 获取传过来的值(即这里 $event 等于 36);在 clickHandle 函数中可以通过形参获取传过来的值 -->
<HelloWorld :age='myAge' @handleMsg='clickHandle' />

export 的作用,与 export default 的区别:

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用 export 关键字输出该变量。

export 的作用:向外暴露对象。export命令对外输出了指定名字的变量(变量也可以是函数或类)。

exportexport default 的区别:

//【export 的使用】:在一个模块中,export、import可以有多个(都需要加大括号),import时要注意变量名(可以取别名)与export时保持一致。
// profile.js
var name = 'zhangsan';
var age = 28;
var func = function() {};
export {name, age, func};

// 对应的导入方式
import { name } from './profile.js';
// 可以使用as关键字,将输入的变量重命名。
import { myName as name } from './profile.js';
// 导入多个
import { name, age, func } from './profile.js';


//【export default 的使用】:在一个模块中,export default、import仅有一个(都不需要加大括号),import时变量名可以取任意名字。
// profile.js
var name = 'zhangsan';
export default name;

// 对应的导入方式
import customName from './profile.js';

路由基础

vue-router的实现原理:路由不同的页面也就是加载不同的组件。

路由的三个基本概念:

  • route:一条路由,如:{ path: '/', component: PageA }
  • routes:一组路由,如:[{ path: '/', component: PageA }, { path: '/pageb', component: PageB }]
  • router:管理路由,一个机制,想当于一个管理者。
  • 客户端中的路由:dom元素显示或隐藏,基于hash和基于HTML5 history api。

vue-router中的路由基于以上4点实现:在vue中我们所有的内容都是组件化的,所有只需要把路径和组件对应起来,然后在组件中把页面渲染出来就可以了。

安装 vue-router:

$ npm install vue-router

页面实现:
在vue-router中,由两个标签<router-view>和<router-link>来对应点击和显示部分;<router-link> 就是定义页面中点击的部分,<router-view> 定义显示部分,就是在点击后匹配的内容显示在什么地方;<router-link>还有一个非常重要的属性to,定义点击之后跳到哪里去。

路由的使用:在src目录下再新建一个 router.js 定义 router

/// -------------------- router.js --------------------
import Vue from 'vue'
import VueRouter from 'vue-router'
// 1. 导入(路由)组件
import PageA from './pages/a.vue'
import PageB from './pages/b.vue'

// 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter),把 VueRouter 加进来,才能进行实例化
Vue.use(VueRouter);

// 2. 定义路由(每个路径映射一个组件)
// 首次进入页面的时候,页面没有内容,因为首次进入页面,它的路径是'/',我们并没有给这个路径做相应的配置,我们要把这个路径指向PageA,用redirect来定义重定向:
const routes = [
    {
        path: "/pagea",
        component: PageA
    },
    {
        path: "/pageb",
        component: PageB
    },
    {
        path: '/',
        redirect: '/pagea'
    }
];

// 3.路由的实例化(最后再将路由的实例和app实例关联)
const router = new VueRouter({
    routes // (缩写) 相当于 routes: routes
});

export default router;

4.把 router 实例注入到vue根实例中,开始使用

import Vue from 'vue'
import App from './App.vue'

// 引入路由
import router from "./router.js"
new Vue({
  el: '#app',
  router,  // 注入到根实例中
  render: h => h(App)
})

5.最后要在 App.vue 中 添加 <router-view></router-view> (将路由匹配到的组件渲染到页面中去)

<template>
  <div class="app">
    <header>
      <!-- 路由的切换:router-link 定义点击导航后到哪个路径下 -->
      <router-link to='/pagea'>进入A页面</router-link>
      <router-link to='/pageb'>进入B页面</router-link>
    </header>
    <!-- 对应的组件内容渲染到router-view中 -->
    <router-view></router-view>   
  </div>
</template>

<script>
export default {
}
</script>

补充:this.$router.push("pagea") 也可以实现路由跳转。

执行过程:当用户点击 router-link 标签时,会去寻找它的 to 属性, 它的 to 属性和 js 中配置的路径{ path: '/pagea', component:PageA} path 一一对应,从而找到了匹配的组件, 最后把组件渲染到 <router-view> 标签所在的地方。所有的这些实现才是基于hash 实现的。

执行流程:项目加载的过程是index.html -> main.js -> app.vue -> index.js -> xxx.vue,如果main.js里面有钩子,会先走钩子。

Vuex基础

vuex是专门用来管理vue.js应用程序中状态(这里所说的状态指的是vue组件中data里面的属性)的一个插件。他的作用是将应用中的所有状态都放在一起,集中式来管理。

把组件的共享状态抽取出来,以一个全局单例模式管理。任何组件都能获取状态或者触发行为。

Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

组件之间的传值:

  • 父组件传子组件:通过子组件的 props 属性
  • 子组件传父组件:通过自定义事件发送消息,传递参数
  • 跨组件传递数据:多个组件之间公用一份数据

安装 Vuex

$ npm install vuex

VueX中的核心内容:
在VueX对象中,其实不止有 state,还有用来操作 state 中数据的方法集,以及当我们需要对 state 中的数据需要加工的方法集等等成员。

成员列表:

  • state 存放状态
  • getters 加工state成员给外界
  • mutations state成员操作
  • actions 异步操作
  • modules 模块化状态管理
/// -------------------- store.js --------------------
import Vue from 'vue'
import Vuex from 'vuex';

// 加载插件
Vue.use(Vuex);

// 1.定义 state (数据部分,相当于组件中的data;数据变化后,会重新渲染到组件中)
const state = {
    count: 1
}

// 2.定义 mutations,在内部对数据进行操作(即修改state中的数据)
const mutations = {
    // 执行这个函数的时候对 count 加 1
    increment(state) {
        state.count++;
    },
    // 执行这个函数的时候对 count 减 1
    decrement(state) {
        state.count--;
    }
}

// 3.定义 actions (actions 是用来接收 vue组件 中的用户行为 dispatch,进一步去触发要做的 commit,通过 commit 来告诉 mutations 要执行哪个操作做数据修改)
const actions = {
    increment: ({ commit }) => {
        // 参数是 mutations 中的函数名
        commit('increment');
    },
    decrement: ({ commit }) => {
        commit('decrement');
    }
}

const store = new Vuex.Store({
    state,       // 存放状态
    // getters,  // state的计算属性(加工state成员给外界)
    mutations,   // 更改state中状态的逻辑,同步操作(state成员操作)
    actions      // 提交mutation,异步操作
    // mudules   // 将store模块化
})

// es6模块导出
export default store;


// 4.注入到 Vue 根实例中
/*
    new Vue({
        store,      // 在根节点注入 store
        render: h => h(App),    // 渲染 App.vue 页面,作为启动页
    }).$mount('#app')
*/

5.在组件中读取或操作 vuex(store.js)中的数据

<!---------------------------- HelloWorld.vue ---------------------------->
<template>
  <div class="hello">
    <!-- 读取 vuex(store.js)中的数据 -->
    vuex {{ $store.state.count }}
    <button type="button" @click="increment">增加</button>
    <button type="button" @click="decrement">删减</button>
  </div>
</template>

<script>

import { mapActions } from 'vuex';

export default {
  // 在组件中分发 Action (关联方法):组件中调用 vuex(store.js)中定义的方法
  // 组件内 increment()函数 映射为 $store.dispatch('increment')
  // 组件内 decrement()函数 映射为 $store.dispatch('decrement')
  methods: mapActions(['increment', 'decrement'])
}
</script>

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