美文网首页
前端-09-vue

前端-09-vue

作者: 西海岸虎皮猫大人 | 来源:发表于2020-09-12 17:56 被阅读0次

    1.概述

    渐进式,可以嵌入到现有程序,也可单独处理
    响应式,数据变化引起视图更新
    可复用组件,拥有自己的js css
    vue更适合快速构建,适合中小型项目
    不支持ie8及以下,支持所有支持es5的浏览器
    调试工具 vue devtools
    默认构建方案webpack
    命令行工具vue cli,市场占有最多是2.x版,本教程基于2.x
    vue兼具angular和react的有点
    文档
    https://cn.vuejs.org/v2/guide/
    vue cli文档
    https://cli.vuejs.org/zh/guide/

    2.环境

    版本,vue-cli改成@vue-cli

    # 安装(速度略慢)
    npm install -g @vue/cli
    # 查看版本
    vue -V
    # 拉取旧版2.x
    npm install -g @vue/cli-init
    # 创建项目
    vue init webpack 01-basic
    # 选择npm
    # 安装依赖
    npm install
    # 启动
    npm start
    # 访问
    http://localhost:8080
    # 目录介绍
    # static 静态文件
    # .babelrc es6解析
    # .postcssrc.js css配置
    

    3.语法

    main.js中创建vue实例

    // 创建实例
    new Vue({
      // id
      el: '#app',
      // 组件
      components: { App },
      // 模板
      template: '<App/>'
    })
    

    HelloWorld.vue

    <template>
      <div class="hello">
        <!--模版语法{{}} 只能存在单行语句-->
        <!--{{ '哈哈' }}-->
        <!--{{ 20 + 1 }}-->
        <!--{{ "OK" ? "yes" : "no" }}-->
        <!--{{ "hello".split("").reverse().join("") }}-->
        <!--{{ msg }}-->
        <!--只渲染一次-->
        <!--<span v-once>{{ msg }}</span>-->
        <!--<div v-html="hello"></div>-->
        <!--绑定属性url-->
        <!--<a v-bind:href="url">{{ url_name }}</a>-->
        <!--<div v-bind:class="divClass">容器</div>-->
        <!--简写-->
        <!--<div :class="divClass">容器</div>-->
        <!--属性可以写表达式-->
        <!--<div :class="div2Class+'-1'"></div>-->
        <!--条件渲染-->
        <div v-if="flag">孙悟空</div>
        <div v-else>六耳猕猴</div>
        <!--<div v-show="flag">真*三国无双</div>-->
    <!--    <ul>
          <li v-for="name in names">{{ name }}</li>
        </ul>-->
        <!-- 遍历复杂对象 -->
    <!--    <ul>
          <li v-for="name in names">{{ name.name }}{{ name.age }}</li>
        </ul>-->
        <!--下标 :key-->
    <!--    <ul>
          <li v-for="(name, index) in names" :key="index">{{index}}&#45;&#45;{{ name.name }}&#45;&#45;{{ name.age }}</li>
        </ul>-->
        <!--遍历对象-->
    <!--    <ul>
          <li v-for="(value, key, index) in obj">{{value}}-{{key}}-{{index}}</li>
        </ul>-->
        <!--事件 传递参数-->
        <button @click="clickHandler('hahaha', $event)">按钮</button>
        <ul>
          <li v-for="item in helloArr">{{ item }}</li>
        </ul>
        <!-- 点击按钮添加数组数据 -->
        <button @click="addHandler">添加数据</button>
      </div>
    </template>
    
    <script>
    export default {
      // v-bind 绑定属性
      // v-if 条件渲染
      // v-show 条件渲染,效果与单一v-if相同
      // v-if 确保子组件销毁重建,高开销
      // v-show总是渲染,通过css控制隐藏
      // v-for 列表渲染
      // 遍历推荐加key
      // 事件
      name: 'HelloWorld',
      methods:{
        clickHandler(data) {
          // 接收参数
          console.log(data);
          console.log(event);
          // 点击按钮改变data数据
          // 引起视图更新
          // this.flag = true;
          this.flag = !this.flag;
        },
        addHandler(event) {
          // 注意数组方法是改变原数组还是创建新数组
          // 变异方法,改变原数组,引起视图变化 push pop...
          // this.helloArr.push("hello4");
          // 创建新数组,不引起视图变化 filter slice concat
          this.helloArr.concat(["hello4", "hello5"]);
        }
      },
      // 相当于react的state
      data () {
        return {
          msg: 'Hello Vue',
          hello: "<h3>Hello H3</h3>",
          url_name: "百度",
          url: "https://www.baidu.com",
          divClass: "isActive",
          div2Class: "list",
          flag: false,
    /*      names: [
            "张三",
            "李四",
            "王五"
          ],*/
          names: [
            {
              name: "zhangsan",
              age: 18
            },
            {
              name: "lisi",
              age: 19
            }
          ],
          obj: {
            name: "vincent",
            age: 18
          },
          helloArr: ["hello1", 'hello2', "hello3"]
        }
      }
    }
    </script>
    
    <style scoped>
    </style>
    
    计算属性 class与style绑定 v-model
    <template>
        <div id="VueDemo">
          <!--重复表达式-->
          <!--{{ getMsg }}-->
          <!--{{ mMsg() }}-->
          <!--<div>-->
            <!--{{ getMsg }}-->
            <!--{{ mMsg() }}-->
          <!--</div>-->
          <!--根据值判断key是否存在-->
          <!--静态class不会被替代-->
          <!--<div class="old" :class="{'active':classDemo, 'txt': classDemo}">-->
            <!--css-->
          <!--</div>-->
    
          <!--<div :class="{'c1':c1, 'c2':c2}">test</div>-->
          <!--<div :class="cssObj">test</div>-->
    
          <!--通过事件改变css-->
          <!--<button @click="changCss">改变css</button>-->
    
          <!--属性数组-->
    <!--      <div :class="[activeClass, errorClass]" >
            test1
          </div>-->
          <!--属性数组 三目表达式-->
          <!--<div :class="[isActive ? activeClass : '', errorClass]">test2</div>-->
          <!--复杂属性表达式-->
          <!--<div class="old" :class="[isActive ? 'active' : 'noActive', {'text': classDemo}]">test3</div>-->
          <!--属性还可以拼接字符串 对象key不能拼接字符串-->
          <!--<div class="old" :class="[isActive ? 'active' : helloC+'noActive', {'text': classDemo}]">test3</div>-->
          <!--内联样式-->
    <!--      <div :style="{color:'red', fontSize:'40px'}">
            style
          </div>-->
    
          <!--v-model双向数据绑定,忽略value-->
    <!--      <div class="myInput">
            <input v-model="inputMsg" type="text">
            <p>{{ inputMsg }}</p>
    
            <input type="checkbox" id="checkbox" v-model="checked">
            <label for="checkbox">{{ checked }}</label>
          </div>-->
    
          <!--修饰符-->
          <!--
            lazy 回车或失去焦点才会绑定
            number 只接收数字类型
            trim 去掉前后空格
          -->
          <!--<input v-model.lazy="inputMsg" type="text">-->
          <!--<input v-model.trim="inputMsg" type="text">-->
          <p>{{ inputMsg }}</p>
          <button @click="changeName">改变myname</button>
        </div>
    </template>
    
    <script>
        export default {
          name: "VueDemo",
          data() {
            return {
              msg: "hello",
              classDemo: true,
              // c1: true,
              // c2: true,
              cssObj: {
                'c1': true,
                'c2': true
              },
              activeClass: 'active',
              errorClass: 'text-danger',
              isActive: false,
              helloC: 'he_',
              inputMsg: '',
              checked: true,
              myname: 'vincent'
            }
          },
          // 实时监听数据变化
          // 计算属性vs watch 不要滥用watch
          watch: {
            inputMsg(data) {
              if(data == '100') {
                console.log(100);
              }
            },
            myname(data) {
              console.log(data)
            }
          },
          // 计算属性
          // 计算属性根据依赖进行缓存
          computed: {
            getMsg() {
              // 如果数据不改变不会重新计算
              return this.msg.split('').reverse().join('')
            }
          },
          methods: {
            // 函数每次调用都会执行
            mMsg() {
              return this.msg.split('').reverse().join('');
            },
            changCss() {
              this.cssObj = {
                c1: false,
                c2: true
              }
            },
            changeName() {
              this.myname += "1";
            }
          }
        }
    </script>
    
    <style scoped>
      .active {
        color: red;
      }
    
      .txt {
        font-size: 30px;
      }
    
    </style>
    
    

    3.组件

    A.vue

    <template>
      <div>
        组件A: {{ msg }}
        <!--切换组件后组件消失-->
        <button @click="change">change</button>
      </div>
    </template>
    
    <script>
        export default {
          name: "A",
          data() {
              return {
                msg: "默认"
              }
          },
          methods: {
            change() {
              this.msg = "改变";
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    B.vue

    <template>
        <div>
          组件B
        </div>
    </template>
    
    <script>
        export default {
            name: "B"
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    Learn.vue

    <template>
      <!--只能存在一个根容器-->
      <div class="container">
        新的组件: {{ title }}
        <button @click="sendMsg">向父组件传递数据</button>
        <input v-model="searchText" type="text">
      </div>
    </template>
    
    <script>
        export default {
          name: "Learn",
          // 所有初始化状态放入data
          data() {
            return {
              searchText: ""
            }
          },
          // 接收父组件传递的数据
          props: ["title"],
          methods: {
            sendMsg() {
              // this.$emit("getMsg", "我是儿子的数据");
              this.$emit("getMsg", this.searchText);
            }
          }
        }
    </script>
    
    <!--样式 scoped限制css只作用于当前组件-->
    <style lang="css" scoped>
    .container {
      background: red;
    }
    </style>
    
    

    App.vue

    <template>
      <div id="app">
        <img src="./assets/logo.png">
        <!--<HelloWorld/>-->
        <!--<VueDemo />-->
        <!--组件传递数据-->
        <!--<Learn title="learn vue"/>-->
    <!--    <input v-model="parentText" type="text">
        <Learn @getMsg="getSonMsg" :title="parentText"/>
        {{ demo }}-->
    <!--    <A />
        <B />-->
    
        <!-- 组件不重复渲染以保存状态 -->
        <keep-alive>
          <component v-bind:is="currentComponent"></component>
        </keep-alive>
        <button @click="changeComponent">切换组件</button>
      </div>
    </template>
    
    <script>
    import HelloWorld from './components/HelloWorld'
    import VueDemo from './components/VueDemo'
    // 引入
    import Learn from './components/Learn'
    import A from './components/A'
    import B from './components/B'
    
    export default {
      name: 'App',
      data() {
        return {
          demo: "",
          parentText: "",
          title: {
            name: "vincent"
          },
          currentComponent: A,
          flag: false
        }
      },
      components: {
        // HelloWorld,
        // VueDemo,
        // 注入
        Learn,
        A,
        B
      },
      methods: {
        getSonMsg(data) {
          this.demo = data;
        },
        changeComponent() {
          if(this.flag) {
            this.flag = false;
            this.currentComponent = A;
          } else {
            this.flag = true;
            this.currentComponent = B;
          }
        }
      }
    }
    </script>
    
    <style>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    

    4.动画 | 自定义指令

    <template>
      <div class="anim">
        <button @click="show = !show">toggle</button>
    <!--    <transition name="fade">
          <p v-if="show">hello</p>
        </transition>-->
    
        <!--自定义名字-->
    <!--    <transition name="vincent">
          &lt;!&ndash;<p v-if="show">hello</p>&ndash;&gt;
          <p class="box" v-if="show"></p>
        </transition>-->
    
        <transition
          name="custom-classes-transition"
          enter-active-class="animated rollIn"
          leave-active-class="animated zoomOutDown"
        >
          <p class="box" v-if="show">hello</p>
        </transition>
    
        <input v-focus type="text">
        <p v-myStyle>哈哈哈</p>
      </div>
    </template>
    
    <script>
        // 动画
        export default {
          name: "Anim",
          data() {
            return {
              show: true
            }
          },
          // 局部指令
          // 很多位置实现相同效果时用指令实现
          directives: {
            focus: {
              inserted: function (el) {
                  el.focus();
              }
            },
            myStyle: {
              inserted: function (el) {
                el.style.fontSize = "40px";
              }
            }
          }
        }
    </script>
    
    <style scoped>
    /*   .fade-enter-active, .fade-leave-active {
        transition: opacity .5s;
      }
      .fade-enter, .fade-leave-to {
        opacity: 0;
      }*/
    
    /*  .vincent-enter, .vincent-leave-to {
        opacity: 0;
      }
      .vincent-leave-to, .vincent-enter {
        opacity: 1;
      }
      .vincent-enter-active, .vincent-leave-active {
        transition: opacity .5s;
      }*/
    
      /* 过渡效果 */
    /*  .vincent-enter, .vincent-leave-to {
        opacity: 0;
        width: 0;
      }
      .vincent-enter-to, .vincent-leave {
        opacity: 1;
        width: 100px;
      }
      .vincent-enter-active, .vincent-leave-active {
        transition: all 1s;
      }
    
      .box {
        width: 100px;
        height: 100px;
        background: red;
      }*/
    
    /*  .vincent-enter, .vincent-leave-to {
      }
      .vincent-enter-to, .vincent-leave {
      }
      .vincent-enter-active {
        animation: bounce-in 1s;
      }
      .vincent-leave-active {
        animation: bounce-out 1s;
      }
    
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(1);
        }
      }
    
      @keyframes bounce-out {
        0% {
          transform: scale(1);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(0);
        }
      }*/
    
    .box {
        width: 100px;
        height: 100px;
        background: red;
      }
    </style>
    

    5.过滤器

    Child.vue

    <template>
      <div>
        {{ money | rmb }}<br>
        {{ txt | author }}
      </div>
    </template>
    
    <script>
        export default {
            name: "FilterDemo",
          data() {
              return {
                money: 20,
                txt: "To be or not to be"
              }
          },
          // 过滤器对变量格式化处理
          filters: {
            rmb: function (value) {
              if(!value) return;
              value = value.toString();
              return "$" + value;
            },
            author: function (value) {
              if(!value) return;
              return value + "--by vincent";
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    6.父子组件 | 插槽

    <template>
        <div class="child">
          {{ getFoo }}
          子组件: {{ title }}--{{ age }}--{{ nick }}--{{ parent }}
          <ul>
            <li v-for="g in girlFriend">{{ g }}</li>
          </ul>
          <button @click="sendMonney">赚了</button>
        </div>
    </template>
    
    <script>
        // 子组件可以读取父组件中的元素,但不建议,破坏耦合性
        // $root全局变量也不建议
        export default {
          name: "Child",
          data() {
            return {
    
            }
          },
          methods: {
            sendMonney() {
              this.$emit("money", "翻了5倍");
            }
          },
          computed: {
            getFoo() {
              return this.$root.foo;
            }
          },
          // props: ["title", "age"]
          // 指定类型
          props: {
            title: String,
            age: Number,
            // 默认值
            nick: {
              type: String,
              default: "国民儿子"
            },
            parent: {
              type: String,
              required: true
            },
            // 默认值如果是数组或对象必须返回function
            girlFriend: {
              type: Array,
              default: function () {
                return  ["凤姐", "芙蓉姐姐"]
              }
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    Parent.vue

    <template>
        <div class="parent">
          <slot ct="我是parent的数据">
          </slot>
          父组件: {{ money }}
          <Child @money="getMoney" parent="王健林" nick="娱乐圈键盘侠" title="一个亿的小目标" :age="age"/>
        </div>
    </template>
    
    <script>
        import Child from './Child'
        import SlotDemo from './SlotDemo'
    
        export default {
          name: "Parent",
          data() {
            return {
              money: "",
              age: 30
            }
          },
          components: {
            Child, SlotDemo
          },
          methods: {
            getMoney(data) {
              this.money = data;
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    SlotDemo.vue

    <template>
    <!--    <div class="slotDemo">
          <slot></slot>
        </div>-->
    
      <div class="slotDemo">
    <!--    <slot name="v1"></slot>
        <slot name="v2">
          我是插槽默认信息
        </slot>-->
        <!--儿子给父亲传数据-->
        <slot ct="我是儿子的数据">
        </slot>
      </div>
    </template>
    
    <script>
        // 插槽,内容分发,传递视图
        // 具名插槽 可以传递多个插槽
        // 插槽可以设置默认信息
        // 插槽可以设置动态内容,注意作用域
        // 插槽的视图是父组件传递的
        // 视图要显示的内容由子组件决定
        // 效果由父组件决定,数据由子组件决定
        export default {
            name: "SlotDemo",
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    7.音乐 | 操作原生DOM

    <template>
        <div class="music">
          音乐实例
          <p class="p1" ref="p1">原生P元素</p>
          <input type="text" ref="myinput">
          <button @click="getValue">按钮</button>
        </div>
    </template>
    
    <script>
        // 操作原生dom
        // 改变内容和属性可以通过绑定数据
        // input标签 audio标签需要通过原生dom操作
        // 没有必要不要操作原生dom,性能不如虚拟dom
        export default {
          name: "Music",
          data() {
              return {
    
              }
          },
          mounted() {
            this.$refs.p1.innerHTML = "改变吧"
            this.$refs.myinput.value = "哈哈"
            console.log(this.$refs.p1)
          },
          methods: {
            getValue() {
              console.log(this.$refs.myinput.value)
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    
    

    8.网络请求

    vue-resource不维护,推荐axios
    axios基于promise
    中文文档
    https://www.kancloud.cn/yunye/axios/234845

    # 安装
    npm install --save axios
    # main.js引入
    import Axios from "axios"
    Vue.prototype.$axios = Axios
    

    main.js

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    import App from './App'
    import Axios from "axios"
    
    Vue.prototype.$axios = Axios
    
    Vue.config.productionTip = false
    
    // 全局配置
    Axios.defaults.baseURL = 'http://localhost';
    Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
    
    // 拦截器
    // 添加请求拦截器
    Axios.interceptors.request.use(function (config) {
      // 在发送请求之前做些什么
      if(config.method === "post"){
        config.data = qs.stringify(config.data)
      }
      return config;
    }, function (error) {
      // 对请求错误做些什么
      return Promise.reject(error);
    });
    
    // 添加响应拦截器
    Axios.interceptors.response.use(function (response) {
      // 对响应数据做点什么
      if(!response.data){
        return {
          msg:"数据返回不合理"
        }
      }
      return response;
    }, function (error) {
      // 对响应错误做点什么
      return Promise.reject(error);
    });
    
    // 自定义全局指令
    Vue.directive('focus', {
      // 常用
      bind:function() {
        console.log("只调用一次")
      },
      // 常用
      // 当前指令生命周期
      // 插入到元素当中时执行
      inserted:function (el, binding) {
        console.log(binding);
        console.log(el);
        el.focus();
      },
      update:function () {
        console.log("更新")
      }
    })
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // 全局变量
      data:{foo: "hellofoo"},
      components: { App },
      template: '<App/>'
    })
    
    
    跨域问题

    使用代理解决
    config/index.js

        // 跨域处理
        proxyTable: {
          '/dfun_api':{
            target: 'http://localhost',
            pathRewrite: {
              '^/dfun_api': ''
            },
            changeOrigin: true
          }
        },
    

    main.js
    注意注掉之前的全局配置

    // 跨域处理
    Vue.prototype.HOST = "/dfun_api"
    

    请求

     var url = this.HOST + "/04_php/basic/b1_ajax.php";
        // post请求, 跨域
        this.$axios.post(url, qs.stringify({
    ...
    

    修改配置文件需要重启服务器

    9 路由

    https://router.vuejs.org/zh/

    # 创建项目
    vue init webpack 03-router
    cd 03-router
    npm install
    
    # 安装路由
    npm install vue-router --save
    
    # main.js 
    # 引入路由,创建路由,注入实例
    ------
    import VueRouter from 'vue-router'
    vue.use(VueRouter)
    
    // 创建路由
    const router = new VueRouter({
      routes: [
        {
          path: '/hello',
          name: 'HelloWorld',
          component: HelloWorld
        }
      ]
    
    new Vue({
      el: '#app',
      // 注入实例对象
      router,
      components: { App },
      template: '<App/>'
    })
    ------
    
    # 显示路由 App.vue
    ------
      <div id="app">
        <!--显示路由-->
        <router-view />
      </div>
    ------
    
    路由嵌套
    编程式导航
     gotoHello() {
          // this.$router.push("/hello")
    
          // 传递对象
          /* this.$router.push({
             'path': '/hello'
           })*/
    
          // replace方式,不会向history添加记录
          this.$router.replace({path: 'hello'})
    
          // 回退到之前一个页面
          this.$router.go(-1)
        }
    

    10 element-ui

    https://element.eleme.cn/#/zh-CN/component
    https://iviewui.com/docs/introduce
    npm i element-ui -S
    npm install babel-plugin-component -D

    11 swipper组件

    https://github.com/surmon-china/vue-awesome-swiper
    https://www.swiper.com.cn/
    npm install swiper vue-awesome-swiper --save
    npm install --save axios

    11 组件库

    https://github.com/vuejs/awesome-vue
    使用见组件文档

    npm install --save vue-awesome-swiper

    npm install --save pull-to-refresh

    相关文章

      网友评论

          本文标题:前端-09-vue

          本文链接:https://www.haomeiwen.com/subject/tugwektx.html