美文网首页
Vue.js 学习笔记(一)数据绑定与指示器

Vue.js 学习笔记(一)数据绑定与指示器

作者: rollingstarky | 来源:发表于2020-01-18 04:10 被阅读0次

    一、安装与项目初始化

    安装 @vue/cli$ npm install -g @vue/cli

    安装 gitsudo apt-get install git

    创建项目:$ vue create todo --default

    项目结构

    $ tree todo -I node_modules
    todo
    ├── babel.config.js
    ├── package.json
    ├── package-lock.json
    ├── public
    │   ├── favicon.ico
    │   └── index.html
    ├── README.md
    └── src
        ├── App.vue
        ├── assets
        │   └── logo.png
        ├── components
        │   └── HelloWorld.vue
        └── main.js
    
    文件 功能
    public/index.html 浏览器加载的 HTML 文件。其中包含用于显示 Vue 应用的标签和加载应用文件的 <script>
    src/main.js 负责 Vue 应用的基本配置,通常还用于注册应用依赖的第三方组件
    src/App.vue Vue 组件,即 Vue 应用的主要构成部分。如需要显示给用户的 HTML 页面、Javascript 代码和 CSS 等

    public/index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>todo</title>
      </head>
      <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    

    src/main.js

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
    }).$mount('#app')
    

    App.vue

    <template>
      <div id="app">
        <img alt="Vue logo" src="./assets/logo.png">
        <HelloWorld msg="Welcome to Your Vue.js App"/>
      </div>
    </template>
    
    <script>
    import HelloWorld from './components/HelloWorld.vue'
    
    export default {
      name: 'app',
      components: {
        HelloWorld
      }
    }
    </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>
    

    开启测试服务器:$ npm run serve

    npm serve

    二、数据绑定与指示器

    准备工作,添加 CSS 库:
    $ npm install bootstrap@4.0.0

    修改 src/main.js 导入 Bootstrap

    // src/main.js
    import Vue from 'vue'
    import App from './App.vue'
    
    import "bootstrap/dist/css/bootstrap.min.css";
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
    }).$mount('#app')
    

    1. 数据绑定

    展示数据给用户是 web 组件最重要的工作之一,数据绑定可以将 <script> 下定义的数据对象关联给 <template> 中的特定元素进行显示。

    显示数据

    修改 src/App.vue 如下:

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak"
        }
      }
    }
    </script>
    

    通过 JavaScript 模块中的 data 属性定义了可以被绑定的数据值:

    data: function () {
      return {
        name: "Kayak"
      }
    }
    

    再借助数据绑定机制,使用 {{ name }} 等语法将 <script> 中定义的数据关联到 template 模块的 HTML 元素中。这种形式的绑定也称为文本注入(text interpolation binding)

    data binding
    绑定中使用表达式

    实际上数据绑定不仅仅可以通过预先定义的变量替换 HTML 元素中的文本,还可以直接嵌入复杂的表达式,具体代码如下:

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
        <h3>Price: ${{ (price + (price * (taxRate / 100))).toFixed(2) }}</h3>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak",
          price: 275,
          taxRate: 12
        }
      }
    }
    </script>
    
    expression
    Computed Properties

    在数据绑定中使用过于复杂的表达式并不利于代码的阅读、维护和复用。为了使 template 组件可以足够简单,Vue 提供了计算属性模块,可以从 data 属性提供的数据中生成需要的值,从而减少数据绑定中复杂表达式的使用。

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
        <h3>Price: ${{ totalPrice.toFixed(2) }}</h3>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak",
          price: 275,
          taxRate: 12
        }
      },
      computed: {
        totalPrice: function() {
          return this.price + (this.price * (this.taxRate / 100));
        }
      }
    }
    </script>
    

    关键代码:

    <h3>Price: ${{ totalPrice.toFixed(2) }}</h3>
    ...
    computed: {
      totalPrice: function() {
        return this.price + (this.price * (this.taxRate / 100));
      }
    }
    
    computed properties
    Methods

    方法相对于计算属性则更加灵活,可以定义自己的参数。

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
        <h4>Price: ${{ lowTotalPrice.toFixed(2) }} (Low Rate)</h4>
        <h4>Price: ${{ highTotalPrice.toFixed(2) }} (High Rate)</h4>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak",
          price: 275,
          lowTaxRate: 12,
          highTaxRate: 20
        }
      },
      computed: {
        lowTotalPrice: function () {
          return this.getTotalPrice(this.lowTaxRate);
        },
        highTotalPrice: function () {
          return this.getTotalPrice(this.highTaxRate);
        }
    },
      methods: {
        getTotalPrice(taxRate) {
          return this.price + (this.price * (taxRate / 100));
        }
      }
    }
    </script>
    
    methods

    也可以省略掉上面代码中对计算属性的定义,直接在数据绑定时调用方法。这显示了各组件之间组织的灵活性,当然此举也会增加 template 中代码的复杂度:

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
        <h4>Price: ${{ getTotalPrice(lowTaxRate).toFixed(2) }} (Low Rate)</h4>
        <h4>Price: ${{ getTotalPrice(highTaxRate).toFixed(2) }} (High Rate)</h4>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak",
          price: 275,
          lowTaxRate: 12,
          highTaxRate: 20
        }
      },
    
      methods: {
        getTotalPrice(taxRate) {
          return this.price + (this.price * (taxRate / 100));
        }
      }
    }
    </script>
    
    methods 2
    Filters

    过滤器是一种在 filter 模块下定义的函数,可以对需要显示的表达式进行格式化操作:

    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name }}</h3>
        <h4>Price: {{ getTotalPrice(lowTaxRate) | currency }} (Low Rate)</h4>
        <h4>Price: {{ getTotalPrice(highTaxRate) | currency }} (High Rate)</h4>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Kayak",
          price: 275,
          lowTaxRate: 12,
          highTaxRate: 20
        }
      },
    
      methods: {
        getTotalPrice(taxRate) {
          return this.price + (this.price * (taxRate / 100));
        }
      },
      filters: {
        currency(value) {
          return new Intl.NumberFormat("en-US",
            { style: "currency", currency: "USD" }).format(value);
        }
      }
    }
    </script>
    
    filters
    复杂 Filter
    // src/App.vue
    <template>
      <div id="app" class="bg-primary text-white text-center m-2 p-3">
        <h3>Product: {{ name | reverse | capitalize }}</h3>
        <h4>Price: {{ getTotalPrice(lowTaxRate) | currency(3) }} (Low Rate)</h4>
        <h4>Price: {{ getTotalPrice(highTaxRate) | currency }} (High Rate)</h4>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          price: 48.95,
          lowTaxRate: 12,
          highTaxRate: 20
        }
      },
    
      methods: {
        getTotalPrice(taxRate) {
          return this.price + (this.price * (taxRate / 100));
        }
      },
      filters: {
        currency(value, places) {
          return new Intl.NumberFormat("en-US",
            {
              style: "currency", currency: "USD",
              minimumFractionDigits: places || 2,
              maximumFractionDigits: places || 2
            }).format(value);
        },
        capitalize(value) {
          return value[0].toUpperCase() + value.slice(1);
        },
        reverse(value) {
          return value.split("").reverse().join("");
        }
      }
    }
    </script>
    
    complex filter

    2. Directives

    指示器 是 template 中可以为 HTML 元素添加 Vue.js 功能的一些特殊属性。比如 v-on:click 指示器可以为 <button> 元素添加对鼠标单击事件的监听,v-text 指示器可以设置某个 HTML 元素的文字内容。

    v-text
    有选择地显示元素

    使用 v-if 指示器控制 HTML 元素的显示与隐藏:

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <div class="bg-primary text-white m-2 p-3">
          <h3>Product: <span v-text="name"></span></h3>
          <h4 v-if="showElements">{{ price }}</h4>
        </div>
        <button v-on:click="handleClick" class="btn btn-primary">
          Press Me
        </button>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          price: 275,
          showElements: true
        }
      },
    
      methods: {
        handleClick() {
          this.showElements = !this.showElements;
        }
      },
    }
    </script>
    

    关键代码:

    <h4 v-if="showElements">{{ price }}</h4>
    <button v-on:click="handleClick" class="btn btn-primary">
    ...
    methods: {
        handleClick() {
          this.showElements = !this.showElements;
        }
      }
    
    v-if v-if2
    v-if-else
    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <div class="bg-primary text-white m-2 p-3">
          <h3 v-if="counter % 3 == 0">Product: {{name}}</h3>
          <h3 v-else-if="counter % 3 == 1">Price: {{price}}</h3>
          <h3 v-else>Category: {{category}}</h3>
        </div>
        <button v-on:click="handleClick" class="btn btn-primary">
          Press Me
        </button>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          price: 275,
          category: "Waterspots",
          counter: 0
        }
      },
    
      methods: {
        handleClick() {
          this.counter++;
        }
      },
    }
    </script>
    
    v-if-else
    v-if-else2
    设置元素的 Class 属性
    v-bind-class

    通过 v-bind:class 设置 HTML 元素的 Class 属性:

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <div class="bg-primary text-white m-2 p-3">
          <h3 v-bind:class="elemClasses">Product: {{name}}</h3>
        </div>
        <button v-on:click="handleClick" class="btn btn-primary">
          Press Me
        </button>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          highlight: false
        }
      },
      computed: {
        elemClasses() {
          return this.highlight
            ? ["bg-light", "text-dark", "display-4"]
            : ["bg-dark", "text-light", "p-2"];
        }
      },
      methods: {
        handleClick() {
          this.highlight = !this.highlight;
        }
      },
    }
    </script>
    
    v-bind-class v-bind-class2
    设置多个属性

    v-bind 指示器可以同时设置 HTML 元素的 class、style 等属性,甚至还可以包括原本不存在的由用户自行定义的属性(如下面代码中的 data-size)。

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <div class="bg-primary text-white m-2 p-3">
          <h3 v-bind="attrValues">Product: {{name}}</h3>
        </div>
        <button v-on:click="handleClick" class="btn btn-primary">
          Press Me
        </button>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          highlight: false
        }
      },
      computed: {
        attrValues() {
          return {
            class: this.highlight ? ["bg-light", "text-dark"] : [],
            style: {
              border: this.highlight ? "5px solid red" : ""
            },
            "data-size": this.highlight ? "big" : "small"
          }
        }
      },
      methods: {
        handleClick() {
          this.highlight = !this.highlight;
        }
      },
    }
    </script>
    <style>
      [data-size=big] { font-size: 40pt; }
      [data-size=small] { font-size: 20pt; }
    </style>
    
    multiple attributes multiple attributes2
    设置 HTMLElement 属性(不常用)
    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <div class="bg-primary text-white m-2 p-3">
          <h3 v-bind:text-content.prop="textContent"></h3>
        </div>
        <button v-on:click="handleClick" class="btn btn-primary">
          Press Me
        </button>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          name: "Lifejacket",
          highlight: false
        }
      },
      computed: {
        textContent() {
          return this.highlight ? "Highlight!" : `Product: ${this.name}`;
        }
      },
      methods: {
        handleClick() {
          this.highlight = !this.highlight;
        }
      },
    }
    </script>
    
    HTMLElement HTMLElement2

    3. Repeater Directive

    Vue.js 中的 v-for 指示器可以用来操作数组格式的数据、生成表格和 Grid 布局等。

    遍历数组

    v-for 指示器可以遍历数组结构中的数据对象并以循环的方式绑定给多个 HTML 元素。

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <h2 class="bg-primary text-while text-center p-3">Products</h2>
        <table class="table table-sm table-bordered table-striped text-left">
          <tr><th>Index</th><th>Name</th><th>Price</th></tr>
          <tbody>
            <tr v-for="(p, i) in products" v-bind:key="p.name">
              <td>{{ i + 1 }}</td>
              <td>{{ p.name }}</td>
              <td>{{ p.price }}</td>
            </tr>
          </tbody>
        </table>
        <div>
          <button v-on:click="handleClick" class="btn btn-primary">
            Press Me
          </button>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
      data: function () {
        return {
          products: [
            { name: "Kayak", price: 275 },
            { name: "Lifejacket", price: 48.95 },
            { name: "Soccer Ball", price: 19.50 },
          ]
        }
      },
      methods: {
        handleClick() {
          this.products.push(this.products.shift());
        }
      },
    }
    </script>
    

    关键代码:

    <tr v-for="(p, i) in products" v-bind:key="p.name">
      <td>{{ i + 1 }}</td>
      <td>{{ p.name }}</td>
      <td>{{ p.price }}</td>
    </tr>
    
    v-for
    遍历对象

    注意此处遍历的数据结构是 JavaScript 对象而不是上面代码中的数组。

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <h2 class="bg-primary text-while text-center p-3">Products</h2>
        <table class="table table-sm table-bordered table-striped text-left">
          <tr><th>Index</th><th>Key</th><th>Name</th><th>Price</th></tr>
          <tbody>
            <tr v-for="(p, key, i) in products" v-bind:key="p.name">
              <td>{{ i + 1 }}</td>
              <td>{{ key }}</td>
              <td>{{ p.name }}</td>
              <td>{{ p.price }}</td>
            </tr>
          </tbody>
        </table>
        <div>
          <button v-on:click="handleClick" class="btn btn-primary">
            Press Me
          </button>
        </div>
      </div>
    </template>
    
    <script>
    import Vue from "vue";
    
    export default {
      name: "app",
      data: function () {
        return {
          products: {
            "kayak": { name: "Kayak", price: 275 },
            22: { name: "Lifejacket", price: 48.95 },
            3: { name: "Soccer Ball", price: 19.50 },
            4: { name: "Corner Flags", price: 39.95 }
          }
        }
      },
      methods: {
        handleClick() {
          Vue.set(this.products, 5, { name: "Running Shoes", price: 100 });
        }
      },
    }
    </script>
    

    PS:更新表格数据应使用 Vue.set(),不要使用类似 this.products[index] = xx 这样的形式。

    Vue.set(this.products, 5, { name: "Running Shoes", price: 100 });
    
    object object2
    v-for 与 Computed Properties

    v-for 指示器可以搭配计算属性和方法等一起使用,参考(细品)下面的分页示例:

    // src/App.vue
    <template>
      <div id="app" class="container-fluid text-center">
        <h2 class="bg-primary text-while text-center p-3">Products</h2>
        <table class="table table-sm table-bordered table-striped text-left">
          <tr><th>Name</th><th>Price</th></tr>
          <tbody>
            <tr v-for="p in pageItems" v-bind:key="p.name">
              <td>{{ p.name }}</td>
              <td>{{ p.price }}</td>
            </tr>
          </tbody>
        </table>
        <div>
          <!-- eslint-disable-next-line vue/require-v-for-key -->
          <button v-for="i in pageCount" v-on:click="selectPage(i)"
                  class="btn btn-secondary m-1"
                  v-bind:class="{'bg-primary': currentPage == i}">
            {{ i }}
          </button>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data: function () {
        return {
          pageSize: 3,
          currentPage: 1,
          products: [
            { name: "Kayak", price: 275  },
            { name: "Lifejacket", price: 48.95  },
            { name: "Soccer Ball", price: 19.50  },
            { name: "Corner Flags", price: 39.95  },
            { name: "Stadium", price: 79500  },
            { name: "Thinking Cap", price: 16  },
            { name: "Unsteady Chair", price: 29.95  },
            { name: "Human Chess Board", price: 75  },
            { name: "Bling Bling King", price: 1200  }
          ]
        }
      },
      computed: {
        pageCount() {
          return Math.ceil(this.products.length / this.pageSize);
        },
        pageItems() {
          let start = (this.currentPage - 1) * this.pageSize;
          return this.products.slice(start, start + this.pageSize);
        }
      },
      methods: {
        selectPage(page) {
          this.currentPage = page;
        }
      }
    }
    </script>
    
    paging paging2

    参考资料

    Pro Vue.js 2

    相关文章

      网友评论

          本文标题:Vue.js 学习笔记(一)数据绑定与指示器

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