vue组件模块化开发入门,单文件组件应用。

表格组件

 还记得上节课(bushi~)上篇文章npm的搭建运行项目吗。这篇用一些组件使用的例子来巩固一下。

首先,用npm init vite@latest新建好你的项目,然后cd进到项目里面,首次创建用npm i安装依赖,接着用npm run dev运行开发环境,点击url即可,当重新打开时,同样cd进入到项目里,然后npm run dev就行了。以上几步操作要记住,后面的vue项目开发会常用到。

进入到项目后,要清楚默认的文件哪些是可以替换的,要是你用的是vite记得把入口文件main.js文件默认的css文件注释掉或删掉,用脚手架cli的好像没有。

import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

由于这里挂载了app根组件,然后vite中index.html渲染页面文件引入了main.js,cli中则是挂载了app根组件节点,因此把你写好的自定义组件放到app根组件里,就可以在页面上显示了。

接着,先看题来定义组件。

1.实现表格的组件写法,实现表格奇偶行显示不同样式的案例,并实现鼠标移入及点击选中变化样式功能。要求:单文件mytable.vue方式,表格样式可以自定义。

展开项目的src目录下的components,然后把默认的vue文件删去,新建一个mytable.vue文件,当然为了养成做项目的习惯,也可以先新建一个mytale文件夹做分类,再新建一个mytable.vue文件,注意,这里的路径要记住,写完要导入到app组件才能显示。

新建完一个空文件后,创建一个单文件vue模板,什么是单文件组件,单文件组件是指将 Vue 组件的模板(HTML)、逻辑(JavaScript)和样式(CSS)都写在一个文件中的方式。其实就是一个组件写你学过的前端三件套,其中template对应html部分,script是js部分,style是css部分。这里可以用一个编译器的插件Vue VSCode Snippets,在新建的文件中输入vbase敲回车即可搭建vue单文件模板了。

<template>
    <div>

    </div>
</template>

<script>
    export default {
        
    }
</script>

<style lang="scss" scoped>

</style>

然后就可以在里面写了,注意样式的scoped表示当前组件使用,比如这题是做表格,安排。

<template>
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>书名</th>
          <th>作者</th>
          <th>价格</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="(item, index) in tableData"
          :key="item.id"
          :class="{
            'odd-row': index % 2 === 0, 
            'even-row': index % 2 !== 0, 
            'hover-row': hoverId === index,
            'selected-row': selectId === index
          }"
          @mouseenter="mEnter(index)"
          @mouseleave="mLeave(index)"
          @click="trClick(index)"
        >
          <td>{{ item.id }}</td>
          <td>{{ item.title }}</td>
          <td>{{ item.author }}</td>
          <td>{{ item.price }}</td>
          <td><button @click="deleteRow(index)">删除</button></td>
        </tr>
      </tbody>
    </table>
  </template>
  
  <script>
  export default {
    name: 'MyTable',
    data() {
      return {
        tableData: [
          { id: 1, title: "高等数学基础", author: "李明", price: "98" },
          { id: 2, title: "Java程序设计", author: "张伟", price: "105" },
          { id: 3,title: "Vue.js从入门到实战",author: "王磊",price: "139",},
        ],
        hoverId: null, // 鼠标悬停时的行索引
        selectId: null, // 被选中的行索引
      };
    },
    methods: {
      mEnter(index) {
        this.hoverId = index; // 鼠标移入时,设置悬停的行索引
      },
      mLeave(index) {
        this.hoverId = null; // 鼠标移出时,清除悬停的行
      },
      trClick(index) {
        this.selectId = index; // 点击行时,设置选中的行索引
      },
      deleteRow(index) {
        // 删除该行数据
        this.tableData.splice(index, 1);
      },
    },
  };
  </script>
  
  <style scoped>
  /* 奇数行背景色 */
  .odd-row {
    background-color: #f2f2f2;
  }
  
  /* 偶数行背景色 */
  .even-row {
    background-color: #d6d6d6;
  }
  
  /* 鼠标悬浮时的行高亮 */
  .hover-row {
    background-color: #bbdefb;
    cursor: pointer;
  }
  
  /* 点击选中行的背景色 */
  .selected-row {
    background-color: #ff9800;
    font-weight: bold;
  }
  
  /* 表头背景色 */
  th {
    background-color: #4caf50; 
    color: #ecf0f1; 
    padding: 10px;
    text-align: center;
  }
  
  /* 表格样式 */
  table {
    margin: 20px;
    width: 50%;
    border-collapse: collapse;
  }
  
  td,
  th {
    border: 1px solid #fff;
    padding: 8px;
    text-align: center;
  }
  
  button {
    padding: 5px 10px;
    cursor: pointer;
  }
  
  button:hover {
    background-color: #d32f2f;
  }
  </style>

写完后别忘了导入app组件。记住,在vue中使用组件分四步:导出组件、导入组件、注册组件、使用组件。定义好文件后做导出export default暴露出去,然后在需要调用该组件的文件如app.vue中进行import,路径要写对,接着在export default组件里写components{}进行局部注册组件,或app.component('component', component)全局注册,注册完之后就可以使用在template里使用这个组件了,组价的双标签可以简写为如<component />,要记好这几步。

<template>
  <div id="app">
    <MyTable />
  </div>
</template>

<script>
import MyTable from './components/mytable/mytable.vue';

export default {
  name: 'App',
  components: {
    MyTable,
  },
};
</script>

<style>
/* 主样式 */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
}
</style>

在控制台的命令行运行npm run dev点进即可看到表格。

 

购物车组件

2.实现下列购物车实现,注意购物车的父子组件间的数据传递,整个购物车是一个父组件,购物车的每个项目是子组件。父组件传送数据给子组件显示,子组件按钮发送数据给父组件。

<template>
  <div class="container">
    <h1>购物车</h1>
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>商品</th>
          <th>价格</th>
          <th>数量</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <CartItem
          v-for="(product, index) in cartProducts"
          :key="index"
          :product="product"
          :index="index"
          @update-quantity="updateQuantity"
          @remove-product="removeProduct"
        />
      </tbody>
      <tfoot>
        <tr>
          <td colspan="5" style="text-align: center; font-weight: bold;">
            总价:{{ totalPrice }}
          </td>
        </tr>
      </tfoot>
    </table>
  </div>
</template>

<script>
import { ref, computed } from 'vue';
import CartItem from './CartItem.vue';

export default {
  components: { CartItem },
  setup() {
    const cartProducts = ref([
        { name: '巧克力慕斯', price: 12.99, quantity: 3 },
        { name: '草莓冰淇淋', price: 8.99, quantity: 2 },
        { name: '蜜桃奶昔', price: 25.00, quantity: 5 }   
    ]);

    const totalPrice = computed(() => {
      return cartProducts.value.reduce((total, product) => {
        return total + product.price * product.quantity;
      }, 0).toFixed(2);
    });

    const updateQuantity = (index, quantity) => {
      cartProducts.value[index].quantity = quantity;
    };

    const removeProduct = (index) => {
      cartProducts.value.splice(index, 1);
    };

    return { cartProducts, totalPrice, updateQuantity, removeProduct };
  },
};
</script>

<style>
.container {
  width: 80%;
  margin: auto;
  padding: 20px;
}

table {
  width: 60%;
  border-collapse: collapse;
}

th, td {
  border: 1px solid #ccc;
  padding: 10px;
  text-align: center;
}

tfoot td {
  font-weight: bold;
}
</style>
<template>
  <tr>
    <td>{{ index + 1 }}</td>
    <td>{{ product.name }}</td>
    <td>{{ product.price }}</td>
    <td>
      <button @click="decrement">-</button>
      {{ product.quantity }}
      <button @click="increment">+</button>
    </td>
    <td>
      <button @click="remove">删除</button>
    </td>
  </tr>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
  props: {
    product: Object,
    index: Number,
  },
  setup(props, { emit }) {
    const increment = () => {
      emit('update-quantity', props.index, props.product.quantity + 1);
    };

    const decrement = () => {
      if (props.product.quantity > 0) {
        emit('update-quantity', props.index, props.product.quantity - 1);
      }
    };

    const remove = () => {
      emit('remove-product', props.index);
    };

    return { increment, decrement, remove };
  },
});
</script>

<style>
button {
  margin: 0 5px;
}
</style>

写完后,记得导入app根组件显示,别忘了使用组件的几个步骤。

<template>
  <div id="app">
    <MyTable />
    <ShoppingCart />
  </div>
</template>

<script>
import MyTable from './components/mytable/mytable.vue';
import ShoppingCart from './components/cart/ShoppingCart.vue';

export default {
  name: 'App',
  components: {
    MyTable,
    ShoppingCart,
  },
};
</script>

<style>
/* 主样式 */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
}
</style>

 

 

这题用了vue3的组合式api ,组合式api能够提高了代码的复用性,使得逻辑更加模块化,便于拆分和维护,适用于更复杂的应用场景,提升了灵活性。

再介绍一个快速导入组件使用的方法,下好vue-official插件,然后长按组件拖到template要使用的位置。

课后习题

1.组件的data为什么必须是一个函数?

在 Vue 组件中,data 必须是一个函数,以确保每个组件实例都有独立的状态。如果 data 直接是一个对象,所有组件实例将共享同一个数据对象,这会导致不同实例之间的数据相互干扰。通过将 data 定义为函数,Vue确保每个组件实例都有自己的独立数据副本,在每次创建组件实例时都会调用该函数,返回一个新的对象,从而保证了数据的独立性和组件的复用性。

2.写出Vue中组件的6种通信方法 。

(1)Props、$emit:

props:父组件通过props向子组件传递数据,实现数据的单向流动,保持数据的一致性和可预测性。

$emit:子组件可以通过触发事件($emit)来向父组件发送消息,父组件通过监听这些事件来响应子组件的行为。

(2)Provide/Inject:

这种方法允许一个祖先组件提供数据或方法,任何其后代组件都可以通过inject来接收这些数据或方法,实现跨组件层级的通信。

(3)Vuex:

Vuex是 Vue.js 的状态管理库,适用于大型应用。可以通过 Vuex 管理共享状态,实现组件之间的数据共享和通信。

(4)$attrs:

在Vue 3中,$attrs 对象包含了所有父组件传递给子组件的非 prop 属性(即类似 HTML 属性的内容)。这使得子组件能够接收任意的传入属性,而无需在组件中显式定义每一个 prop,为组件间的灵活数据传递提供了便利。

(5)Slots插槽:

插槽允许父组件向子组件的模板内部插入HTML或其他Vue组件,是一种灵活的内容分发机制,特别适用于组件库和高级布局设计。

(6)$refs和$parent/$children/$root:

$refs:用于直接访问组件内的DOM节点或子组件实例,常用于需要直接操作DOM或调用子组件方法的场景。

$parent/$children /$root:这些实例属性允许 组件直接访问其父组件、子组件列表或根组件实例,适用于直接的组件实例操作,但应谨慎使用以避免增加组件间的耦合。

3.怎么理解单向数据流,父子之间传值的方式是什么?

单向数据流是指数据只能从父组件流向子组件,确保了数据流动的可控性和可预测性。父子之间传值的方式主要有两种:

父组件向子组件传值: 使用 props,父组件通过 props 向子组件传递数据,子组件接收并使用这些数据,但不能直接修改。

子组件向父组件传值: 子组件通过 $emit 触发事件,通知父组件更新数据或进行操作,父组件通过监听事件来接收并处理这些数据。 

实验心得

 写剧本写到慌的。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部