Kaze
Kaze
Published on 2022-03-24 / 83 Visits
0
0

Vue入门

一、Vue介绍

1. 安装
1. 安装node.js
2. 终端执行命令
	2.1 npm install -g cnpm --registry=https://registry.npm.taobao.or
 	2.2 cnpm install -g @vue/cli
3. 运行vue项目 npm run serve
2. vue项目结构
  • img

  • ①:assets:存放项目中需要用到的资源文件,css、js、images 等。

    ②:componets: 存放 vue 开发中一些公共组件:例如项目初始的 header.vue、footer.vue 就是公共组件。

    ③:router:vue 路由的配置文件。

    ④:views:存放页面文件

    ⑤:app.vue:根组件

    ⑥:main.js:项目的入口文件,定义了 vue 实例,并引入根组件 app.vue,将其挂载到 index.html 中 id 为‘app’的节点上。

二、Vue双向绑定

1. 声明式渲染
  • 每一个 Vue 文件由三部分组成,templatescriptstyle ,它们分别对应 HTMLJavaScriptCSS

  • template 里面只允许有一个块状元素,通常情况下在 template 里面写的都是 divtemplate标签内只能有一个块标签,其他所有的标签都在这个块标签内

  • <template>
      	<h2>{{title}}</h2>
        <ul class="list">
          <li>{{todoList[0]}}</li>
    	</ul>
    </template>
    
    <script>
      // export default是固定格式,不需要纠结
      export default {
        // 模块的名字
        name: "app",
        // 页面中数据存放的地方
        data() {
          return {
            title: "优课达--学的比别人好一点",
            todoList: [
            "完成HTML标签学习",
            "完成CSS文字样式学习",
            "完成CSS盒模型学习",
            "完成Flex布局学习",
            "完成JavaScript入门学习"
           ]
          };
        }
      };
    </script>
    
    <style lang="scss" scope>
        
    </style>
    //使用sass语法,需要在style标签上添加lang="scss"
    //scope 你可以理解为锁,将 css 代码锁在本文件内,只在本文件内有用
    
2. 处理用户输入
  • 双向绑定v-model:

    <input type="text" v-model="message" />,则改变message变量的值,input标签内的值会变化,改变input标签内的值,message变量的值也会改变。

3. 处理用户事件
  • 事件绑定:v-on:

    <button v-on:click="add">按钮</button> or <button @click="add">按钮</button>

  • methods(方法):

    <script>
    export default{
        name:"app",
        methods:{
            alertFn:function(){
                alert("Hello World");
            }
        }
    }
    </script>
    
  • 事件修饰符:

    • 阻止冒泡事件

      <div @click.stop="fn2"></div>

    • 捕获事件

      <div class="div2" @click.capture="fn2"></div>

    • 阻止默认事件

      <div class="div2" @click.prevent="fn2"></div>

4. 监听数据变化
  • 侦听器watch:

    <script>
      export default {
        name: "app",
        watch: {
          firstName: {
            handler: function (newName, oldName) {
                // 第一个参数为新值,第二个参数为旧值,不能调换顺序,newName等于handler,对newName进行操作handler也会变化
              this.fullName = newName + " " + this.lastName;
            },
            immediate: true
          }
        }
      };
    </script>
    
  • 侦听器侦听的是data里面的变量,当变量发生变化的时候,侦听器开始运行

  • 侦听器的名称一定要跟被侦听的变量名一致

  • 当我们侦听的值没有发生改变的时候,是不会触发侦听器的,并且,页面第一次渲染的时候也不会触发侦听器。当immediate属性的值为true时,不论数据是否变化,页面刷新以后,handler方法就会运行。

三、Vue Template

1. HTML 属性渲染语法
  • 属性动态绑定v-bind:

    • v-bind 的简写模式,即冒号 :

    • 如:

      <img src="#" :alt="变量" />
      
2. 模板中使用表达式
  • 差值表达式中不仅可以写一个变量,还可以进行简单的计算,可以使用三元表达式进行判断,在模版中还可以使用 js 内置方法。不要在模版中滥用表达式,尤其是 js 的内置方法。否则会使 html 代码变得臃肿。
3. 条件渲染语句
  • v-if:当条件满足的时候,会显示标签内的内容。

    <p v-if="isShow()">{{ message }}</p>
    
  • v-else:不符合if的时候,就显示else中的内容。

    <p v-if="isShow()">{{ message }}</p>
    <p v-else>{{ defaultMessage }}</p>
    
  • v-else-if:当条件不止一个的时候,就可以用到v-else-if。

    <p v-if="questions[0].type==='PICK'">{{ questions[0].content }}</p>
    <p v-else-if="questions[1].type==='MULT'">{{ questions[1].content }}</p>
    <p v-else>题目还没有加载进来...</p>
    //当前两个都不满足条件时第三个才起作用
    
  • v-show:v-show的用法和v-if是一样的,即当条件满足,就会显示标签中的内容

    • 区别:

      • v-show指令只是将标签的display属性设置为none

      • v-if指令,如果不满足条件,则此标签在dom中根本不存在。

    • 用法:当标签出现以后就不会再次消失,或者消失/出现的频率不高,就用v-if。 当标签会被频繁的切换,从消失到显示,那么就用v-show。

    • 不用太纠结这个,因为开发中大多数情况下都会用v-if。只不过有些人比较严谨,会考虑切换开销和渲染开销。

4. 列表渲染语句
  • v-for:

    • 循环渲染数字<li v-for="item in 5" :key="item">{{ item }}</li>

    • 循环渲染数组<li v-for="(item,index) in nameList" :key="items">{{ item }}</li>

    • 循环渲染对象<li v-for="(value,key,index) in book" :key="key"></li>

    • 遍历数组中的对象

      <li v-for="(item,index) in books" :key="item">
      {{ index+1 }}----{{ item.title }}----{{ item.author }}----{{ item.publishedTime }}
      </li>
      
    • key属性可以用来提升v-for渲染的效率,是每个元素的唯一标识,一一对应,不可更改

四、Vue 动态 Class、Style

1. 计算属性
  • <script>
      export default {
          name: "app",
          data:function(){
              return {
                  message:"优课达--学的比别人好一点~"
              }
          }
          // 计算属性
          computed:{
              reverseMessage:function(){
                  return this.message.split('').reverse().join('')
              }
          }
      };
    </script>
    
  • 计算属性的两个性质:依赖缓存

    • **依赖:**案例中message就是计算属性的依赖

    • **缓存:**上一次计算得到的值

    • message 发生改变(即依赖变化),reverseMessage 计算属性会重新计算,然后返回计算结果;

      message 不发生改变(即依赖不变化),reverseMessage 计算属性会返回缓存的值,而不会重新计算。

  • 计算属性避免了不必要的代码执行,性能更优

  • 计算属性一定要有返回值,但方法不一定要有返回值

2. 动态class
  • 动态样式绑定

    • <div class="base" v-bind:class="{ active: isActive,base:true}"></div>
      
    • <div v-bind:class="['red-style', 'font-style',{'redbg':isChoosed}]"></div>
      
    • <div
        v-bind:class="['red-style', 'font-style',isChoosed ? 'redbg' : 'bluebg']"
      ></div>
      
    • active是类名,对应CSS样式中的类名;如果是带连字符的类名base-active,就需要用单引号引起来

    • isActiveboolean类型的值,决定是否应用该类名。可以是变量、方法、表达式

3. 动态style
  • <div :style="{background:'red','font-weight':700,'font-size':'20px'}"></div>
    
  • <div :style="flexStyle"></div>
    
    data:function(){
        return {
            flexStyle: {
                display: 'flex',
                flexDirection: 'column',
                justifyContent: 'space-between',
                alignItems: 'center',
                flexWrap: 'no-wrap',
            },
        }
    }
    
  • data() {
      return {
        fontStyle: { color: 'red', fontSize: '33px' },
        boxStyle:{width: '200px', height: '200px', border: `1px solid black`}
      };
    },
        
    <div :style="[fontStyle,boxStyle]">这里是文字</div>
    
  • <div :style="[fontStyle, isActive ? boxStyle : colorBox]">这里是文字</div>
    

五、Vue组件

1. 自定义组件
  • 通常一个应用会以一棵嵌套的组件树的形式来组织:

    img

  • 组件的局部注册

    img

  • 重复使用的组件间的 data 是相互独立的。

2. 组件单向数据流
  • 当父组件给子组件的 prop 传递一个值的时候,这个值就变成了子组件实例的一个属性。

    //在父组件中
    <template>
      <div id="app">
        <!-- 注意!title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名 -->
        <HelloVue :title="title1"></HelloVue>
        <HelloVue :title="title2"></HelloVue>
      </div>
    </template>
    
    //在子组件中
    <script>
      export default {
        name: 'HelloVue',
        // 第一步:在 prop 属性中接收 title
        props: ['title']
      };
    </script>
    //带类型声明
    props: {
      title: String,
      // 多类型
      likes: [String, Number],
      // 带有默认值
      isPublished: {
        type: Boolean,
        default: true
      },
      // 必填
      commentIds: {
        type: Array,
        required: true
      },
      author: Object,
      callback: Function,
      contactsPromise: Promise
    }
    
  • 单向数据流指的是父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。子组件不能直接修改父组件通过 prop 传递过来的数据

  • 若需要对传到子组件中的数据进行处理,有两种方法:

    • 计算属性

      props: ['initialTitle'],
      computed: {
        normalizedTitle: function () {
          // 对传入的 initialTitle 进行去空格、字母小写化处理
          return this.initialTitle.trim().toLowerCase()
        }
      }
      
    • prop 传入的数据作为本地数据使用

      props: ['initialTitle'],
      data: function () {
        return {
          // 要读取 prop 里的 initialTitle,要在前面加 “this.”
          // 将传入的 initialTitle 作为本地属性 title 的初始值
          title: this.initialTitle
        }
      }
      
3. 自定义组件绑定原生事件
  • 事件修饰符

    • 当给父组件中的子组件标签绑定事件时,只有子组件的事件会触发,现在要让父组件的子组件标签的事件也能被执行,我们可以添加 .native 修饰符:@click.native="print(article)"

    • .stop 阻止冒泡
      .prevent 阻止标签默认行为
      .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
      .self 只当在 event.target 是当前元素自身时触发处理函数
      .once 事件将只会触发一次
      .passive 告诉浏览器你不想阻止事件的默认行为
      
  • 按键修饰符

    • 回车键监听:<button @keyup.enter="print(article)">按回车键执行 print</button>

      因为回车键(enter 键)的 ASCII 码是 13,所以也可以这样写:

      <button @keyup.13="print(article)">按回车键执行 print</button>

4. 自定义事件
    1. 给子组件 Article.vue 绑定自定义事件:

      在 App.vue 中用 v-on:upVote="handleLikes" 给 Article.vue 绑定自定义事件

    2. 在 Article.vue 中调用自定义事件 “upVote” :

      <button @click="$emit('upVote')">点赞</button>

    3. 如果在点赞的同时还有其他要执行的代码可以这样写:

      methods: {
        childEvent: function() {
          // 调用自定义事件 upVote,这里的第二个参数最后会传到父组件中的 handleLikes 方法里
          this.$emit('upVote', this.article);
          // do other things
        }
      }
      
      <button @click="childEvent">点赞</button>
      
  • 自定义事件的参数:

    • prop 可以完成父组件到子组件的数据传递,自定义事件则可以帮我们完成子组件到父组件的数据传递(通过方法参数)。
  • 自定义事件中的双向绑定

    • <MyCount class="count" :count.sync="count"></MyCount>
      
    • <div class="my-count">
        <button @click="$emit('update:count', count+1)">加一</button>
        {{ count }}
      </div>
      //子组件 MyCount.vue 中用 update:count 的模式触发事件,把 count+1 赋值给 count:
      
    • 虽然 count 是定义在 App.vue 里的,但是通过双向绑定,我们在子组件中改变 count 值,App.vue 里的 count 值也会有相同的变化。

5. 组件函数调用
  • 调用子组件中的方法

    • 给要访问的子组件添加 ref 属性

      <template>
        <Modal ref="modal"></Modal>
      </template>
      
    • 通过 this.$refs.modal 来访问自定义组件 Modal.vue。

    • // 调用子组件中的 show 方法
       this.$refs.modal.show();
      
  • ref 访问子元素

    • <template>
        <div id="app">
          <input ref="input" type="text" />
          <button @click="focusInput">点击使输入框获取焦点</button>
        </div>
      </template>
      
      <script>
      export default {
        name: 'app',
        methods: {
          focusInput() {
            // this.$refs.input 访问输入框元素,并调用 focus() 方法使其获取焦点
            this.$refs.input.focus();
          }
        }
      }
      </script>
      
6. 组件 slot 入门
  • 在子组件 Modal.vue 中用 slot 标签预留一个位置,slot 标签中的内容是后备内容,也可以为空:

  • 后备内容:当父组件不在插槽里添加内容时,插槽显示的内容。

  • 在父组件中使用子组件,并给插槽加入个性化内容:

    <Modal :visible.sync="visible">个性化内容</Modal>
    
7. 组件 slot 进阶
  • <slot> 元素有一个特殊的属性:name。这个属性可以用来定义额外的插槽:

    <div class="modal" v-if="visible">
      <div class="modal-content">
        <header>
          <slot name="header"></slot>
        </header>
        <main>
          <slot></slot>
        </main>
        <footer>
          <slot name="footer"></slot>
        </footer>
      </div>
    </div>
    
  • 没有写 name 属性,但其实它会自动带隐含的名字“default”,也就是我们所说的“匿名插槽”,而带有 name 属性的插槽,我们称为“具名插槽”。

  • 在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

    <Modal :visible.sync="visible">
      <template v-slot:header>
        <h1>Modal title</h1>
      </template>
      
      <template>
          <div>main content</div>
          <div>main content</div>
      </template>
    
      <template v-slot:footer>
        <p>Modal footer</p>
      </template>
    </Modal>
    

六、Vue-router

1. 安装、配置 Router
  • 配置路由:在router/index.js中进行如下配置

    // 0. 导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    Vue.use(VueRouter)
    
    // 1. 定义 (路由) 组件。
    // 可以从其他文件 import 进来
    import Foo from "./views/Foo.vue";
    import Bar from "./views/Bar.vue";
    
    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    const routes = [
      { path: '/foo', component: Foo },
      { path: '/bar', component: Bar }
    ]
    
    // 3. 创建 router 实例,然后传 `routes` 配置
    export default new VueRouter({
      mode: 'history',
      routes // (缩写) 相当于 routes: routes
    })
    
    // 4. 在 main.js 中创建和挂载根实例
    
  • 路由的使用:

    • 在 App.vue 中我们使用 <router-link> 组件来导航:

      <template>
        <div id="app">
          <h1>Hello App!</h1>
          <p>
            <!-- 使用 router-link 组件来导航. -->
            <!-- 通过传入 `to` 属性指定链接. -->
            <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/foo">Go to Foo</router-link>
            <router-link to="/bar">Go to Bar</router-link>
          </p>
          <!-- 路由出口 -->
          <!-- 路由匹配到的组件将渲染在这里 -->
          <!-- App.vue 中的 <router-view></router-view> 是路由的最高级出口 -->
          <router-view></router-view>
        </div>
      </template>
      
2. 添加路由
  • const routes = [
      { path: '/foo',
        name: 'fooName',
        component: () => import('./views/Foo.vue')
      }
    ];
    
  • 通过命名跳转:

    <!-- to 的值是一个对象而不是字符串,所以要在 to 前加 : -->
    <router-link :to="{name: 'fooName'}">Go to Foo</router-link>
    
3. 路由布局管理
  • 定义嵌套路由:

    const routes = [
      {
        path: '/album',
        component: Album,
        // children 属性可以用来配置下一级路由(子路由)
        children: [
          { path: '', component: Empty },
          { path: 'list', component: List },
          { path: 'add', component: Add }
        ]
      }
    ];
    
  • App.vue 中的 <router-view> 对应 routes 里的第一层路由,Album.vue 组件中的 <router-view> 对应 routes 里的第二层路由。

4. 动态路由
  • 动态路由即符合某种模式的多个路径映射到同一个组件

  • import User from "./views/User.vue";
    
    const routes = [
      // id 就是路径参数
      { path: '/user/:id', component: User }
    ]
    
  • id 为路径参数,一个“路径参数”前需要使用冒号 : 标记。当 url 匹配到路由中的一个路径时,参数值会被设置到 this.$route.params 里,可以在组件内读取到。比如 /user/456 匹配的就是 /user/:id,那么这个用户的 id 就是 456,this.$route.params.id 的值就是 456。

  • 捕获 404 页面:

    import NotFound from "./views/NotFound.vue";
    
    const routes = [
      {
        // 会匹配所有路径
        path: '*',
        component: NotFound
      }
    ]
    
  • 当使用通配符路由时,请确保含有通配符的路由应该放在最后。因为路由的匹配通常是根据注册的顺序匹配的,如果 path: '*' 路由放在最前面,那么所有的页面都会因为先匹配到通配符路由而由 NotFound 组件渲染。

  • 当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 的参数。它包含了 URL 通过通配符被匹配的部分

5. 页面跳转
  • $router.push 方法的参数可以是一个字符串路径:

    $router.push('user')
    $router.push('/user')
    /// 意味着匹配根路由,所以 '/user' 这样的写法不管原路径 localhost:8080/?? 中的 ?? 是什么,跳转后 url 都会变为 localhost:8080/user。
    
  • router.push 方法的参数可以是一个描述地址的对象:

    // 对象
    // 这种写法和字符串类型的参数一样
    $router.push({ path: 'home' })
    
    // 命名的路由
    $router.push({ name: 'user', params: { userId: '123' }})
    
    // 带查询参数,变成 /register?plan=private
    $router.push({ path: 'register', query: { plan: 'private' }})
    
  • 参数传递的对应规则:

    • name 对应 params,路径形式:user/123;
    • path 对应 query,路径形式:user?id=123;
  • 获取参数:

    • 可以通过 $route 访问当前路由

    • // $route
      {
        // 路由名称
        name: "user",
        meta: {},
        // 路由path
        path: "/user/123",
        // 网页位置指定标识符
        hash: "#abc",
        // window.location.search
        query: {name: "userName"},
        // 路径参数 user/:userId
        params: {userId: "123"},
        fullPath: "/user/123?name=userName#abc"
      }
      
    • 页面跳转后获取参数可以很方便的通过 $route.query$route.params$route.hash 获取。

6. 重定向和别名
  • 别名:

    • 当访问 /a 时,渲染的是 A;当访问 /b 时,就像访问 /a 一样,渲染的也是 A。

      const routes: [
        // 定义 alias 属性
        { path: '/a', alias: '/b', component: A }
      ];
      
  • 重定向:

    • 若路由 /a 重定向到 /b,即访问 /a 时, url 会自动跳到 /b,然后匹配路由 /b

      const routes: [
        // 定义 redirect 属性,将 /a 重定向到 /b
        { path: '/a', redirect: '/b' }
      ]
      
  • 监听路由:

    • 监听路由 $route 的变化

      watch: {
        $route(to,from){
          console.log(to, from);
        }
      }
      
      // 或者
      watch: {
        $route: {
          handler: function(to,from) {
            console.log(to,from);
          },
          // 深度观察监听
          deep: true
        }
      },
      //to 是变化后的路由,from 是变化前的路由。一般我们用第一种写法就可以了。
      
    • mounted:

      • mounted() {
          this.updateTab()
        },
        methods: {
          updateTab() {
            this.tabList.map(menu => {
              menu.active = menu.type === this.$route.query.courseType || 'JAVA'
            })
          }
        }
        
      • mounted是vue中的一个钩子函数,一般在初始化页面完成后,再对dom节点进行相关操作。通常是为 metheds 函数提前定义( 类似提前声明变量 进入页面内容全部渲染完成后自动引用函数)

    • created:

      • vue.jscreated方法是一个生命周期钩子函数,一个vue实例被生成后会调用这个函数。一般可以在created函数中调用ajax获取页面初始化所需的数据。
7. 网络请求 async 与 await
  • “异步” async:

    • async 是“异步”的简写,用于申明一个异步 function,而这个 async 函数返回的是一个 Promise 对象。
  • “等待异步” await:

    • await 用于等待一个异步方法执行的完成,它会阻塞后面的代码,等着 Promise 对象 resolve ,然后得到 resolve 的值,作为 await 表达式的运算结果:

      async function getAsyncFn() {
        const result = await asyncFn();
        console.log(result);
      }
      
      getAsyncFn();
      
    • 要注意,await 只能出现在 async 函数中,如果用在普通函数里,就会报错。

  • 多个请求并发执行 Promise.all:

    • async function asyncFn1() {
        return "优课达";
      }
      
      async function asyncFn2() {
        return "学的比别人好一点";
      }
      
      async function getAsyncFn() {
        const result = await Promise.all([asyncFn1(), asyncFn2()]);
        console.log(result);
      }
      
      getAsyncFn();
      
  • 运用 async 和 await 请求数据:

    • <script>
      export default {
        data: function() {
          return {
            courseList: []
          };
        },
        async mounted() {
          // 在生命周期 mounted 中调用获取课程信息的方法
          await this.queryAllCourse();
        },
        methods: {
          // 在 methods 对象中定义一个 async 异步函数
          async queryAllCourse() {
            // 在 fetch 中传入接口地址
            const res = await fetch('https://www.fastmock.site/mock/2c5613db3f13a5c02f552c9bb7e6620b/f5/api/queryallcourse');
            // 将文本体解析为 JSON 格式的promise对象
            const myJson = await res.json();
            // 获取返回数据中的 data 赋值给 courseList
            this.courseList = myJson.data;
          }
        }
      }
      </script>
      

Comment