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

2020-07-22  本文已影响0人  irenb

知识点

环境搭建

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

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 插值,绝不要对用户提供的内容使用插值。

<input type="button" value="按钮" v-bind:title="mytitle">
<!-- "v-bind:" 可以缩写成 ":" -->
<input type="button" value="按钮" :title="mytitle">
<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-show 的特点:每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式 -->
<h1 v-if="flag">这是用v-if控制的元素</h1>
<h1 v-show="flag">这是用v-show控制的元素</h1>
<!-- 遍历数组: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>
<script>
export default {
  name: 'HelloWorld',

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

  data() {
    return {
      name: '张三'
    }
  }
}
</script>
<!-- 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的实现原理:路由不同的页面也就是加载不同的组件。

路由的三个基本概念:

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 的细粒度数据响应机制来进行高效的状态更新。

组件之间的传值:

安装 Vuex

$ npm install vuex

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

成员列表:

/// -------------------- 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>
上一篇下一篇

猜你喜欢

热点阅读