ホームページ  >  記事  >  バックエンド開発  >  Vue.js のベスト プラクティス (Vue.js マスターになるための 5 つのヒント)

Vue.js のベスト プラクティス (Vue.js マスターになるための 5 つのヒント)

不言
不言オリジナル
2018-03-30 13:22:041997ブラウズ

この記事は主に、Vue.js のベスト プラクティス、Vue.js マスターになるための 5 つのヒントを共有することを目的としています。困っている友人の助けになれば幸いです

ほとんどの人にとって、Vue.js の基本をマスターした後APIを利用すれば、普通にフロントエンドWebサイトを開発することができます。しかし、Vue を使用してより効率的に開発し、Vue.js のマスターになりたい場合は、これから説明する 5 つのコツを真剣に勉強する必要があります。

最初のステップ: 複雑さを簡素化するウォッチャー

シーン復元:

created(){
    this.fetchPostList()
},
watch: {
    searchInputValue(){
        this.fetchPostList()
    }
}

コンポーネントが作成されると、リストを一度取得し、同時に入力ボックスを監視し、フィルターされたリストを毎回再取得しますシナリオは非常に一般的ですが、それを最適化する方法はありますか?

ムーブ分析:
まず第一に、ウォッチャーでは関数のリテラル名を直接使用できます。次に、immediate: true を宣言すると、コンポーネントの作成時にすぐに実行されることを意味します。

watch: {
    searchInputValue:{
        handler: 'fetchPostList',
        immediate: true
    }
}

第 2 の動き: コンポーネントの登録を一度だけ行います

シーンの復元:

import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}
<BaseInput
  v-model="searchText"
  @keydown.enter="search"
/>
<BaseButton @click="search">
  <BaseIcon name="search"/>
</BaseButton>

基本的な UI コンポーネントを大量に作成しました。これらのコンポーネントを使用する必要があるたびに、最初にインポートし、次にインポートする必要があります。コンポーネントを宣言するのは非常に面倒です。できることなら怠けるという原則を守り、最適化する方法を見つけなければなりません。

ムーブ分析:
自動動的な require コンポーネントを実現するには、アーティファクト Webpack を使用し、require.context() メソッドを使用して独自の (モジュール) コンテキストを作成する必要があります。このメソッドは、検索するフォルダー ディレクトリ、そのサブディレクトリも検索するかどうか、およびファイルと一致する正規表現の 3 つのパラメーターを受け取ります。 require.context() 方法来创建自己的(模块)上下文,从而实现自动动态require组件。这个方法需要3个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。

我们在components文件夹添加一个叫global.js的文件,在这个文件里借助webpack动态将需要的基础组件统统打包进来。

import Vue from 'vue'

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1)
}

const requireComponent = require.context(
  '.', false, /\.vue$/
   //找到components文件夹下以.vue命名的文件
)

requireComponent.keys().forEach(fileName => {
  const componentConfig = requireComponent(fileName)

  const componentName = capitalizeFirstLetter(
    fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
    //因为得到的filename格式是: './baseButton.vue', 所以这里我们去掉头和尾,只保留真正的文件名
  )

  Vue.component(componentName, componentConfig.default || componentConfig)
})

最后我们在main.js中import 'components/global.js',然后我们就可以随时随地使用这些基础组件,无需手动引入了。

第三招:釜底抽薪的router key

场景还原:
下面这个场景真的是伤透了很多程序员的心...先默认大家用的是Vue-router来实现路由的控制。
假设我们在写一个博客网站,需求是从/post-page/a,跳转到/post-page/b。然后我们惊人的发现,页面跳转后数据竟然没更新?!原因是vue-router"智能地"发现这是同一个组件,然后它就决定要复用这个组件,所以你在created函数里写的方法压根就没执行。通常的解决方案是监听$route的变化来初始化数据,如下:

data() {
  return {
    loading: false,
    error: null,
    post: null
  }
}, 
watch: {
  '$route': {
    handler: 'resetData',
    immediate: true
  }
},
methods: {
  resetData() {
    this.loading = false
    this.error = null
    this.post = null
    this.getPost(this.$route.params.id)
  },
  getPost(id){

  }
}

bug是解决了,可每次这么写也太不优雅了吧?秉持着能偷懒则偷懒的原则,我们希望代码这样写:

data() {
  return {
    loading: false,
    error: null,
    post: null
  }
},
created () {
  this.getPost(this.$route.params.id)
},
methods () {
  getPost(postId) {
    // ...
  }
}

招式解析:

那要怎么样才能实现这样的效果呢,答案是给router-view添加一个unique的key,这样即使是公用组件,只要url变化了,就一定会重新创建这个组件。(虽然损失了一丢丢性能,但避免了无限的bug)。同时,注意我将key直接设置为路由的完整路径,一举两得。

<router-view :key="$route.fullpath"></router-view>

第四招: 无所不能的render函数

场景还原:
vue要求每一个组件都只能有一个根元素,当你有多个根元素时,vue就会给你报错

<template>
  <li
    v-for="route in routes"
    :key="route.name"
  >
    <router-link :to="route">
      {{ route.title }}
    </router-link>
  </li>
</template>


 ERROR - Component template should contain exactly one root element. 
    If you are using v-if on multiple elements, use v-else-if 
    to chain them instead.

招式解析:
那有没有办法化解呢,答案是有的,只不过这时候我们需要使用render()函数来创建HTML,而不是template。其实用js来生成html的好处就是极度的灵活功能强大,而且你不需要去学习使用vue的那些功能有限的指令API,比如v-for, v-if。(reactjs就完全丢弃了template)

functional: true,
render(h, { props }) {
  return props.routes.map(route =>
    <li key={route.name}>
      <router-link to={route}>
        {route.title}
      </router-link>
    </li>
  )
}

第五招:无招胜有招的高阶组件

划重点:这一招威力无穷,请务必掌握
当我们写组件的时候,父子组件的通信很重要。通常我们都需要从父组件传递一系列的props到子组件,同时父组件监听子组件emit过来的一系列事件。举例子:

//父组件
<BaseInput 
    :value="value"
    label="密码" 
    placeholder="请填写密码"
    @input="handleInput"
    @focus="handleFocus>
</BaseInput>

//子组件
<template>
  <label>
    {{ label }}
    <input
      :value="value"
      :placeholder="placeholder"
      @focus=$emit(&#39;focus&#39;, $event)"
      @input="$emit(&#39;input&#39;, $event.target.value)"
    >
  </label>
</template>

有下面几个优化点:

1.每一个从父组件传到子组件的props,我们都得在子组件的Props中显式的声明才能使用。这样一来,我们的子组件每次都需要申明一大堆props, 而类似placeholer这种dom原生的property我们其实完全可以直接从父传到子,无需声明。方法如下:

    <input
      :value="value"
      v-bind="$attrs"
      @input="$emit(&#39;input&#39;, $event.target.value)"
    >

$attrs包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定,并且可以通过 v-bind="$attrs" 传入内部组件——在创建更高层次的组件时非常有用。

2.注意到子组件的@focus=$emit('focus', $event)"

global.js というファイルをコンポーネントフォルダーに追加し、webpack を使用して、必要なすべての基本コンポーネントをこのファイルに動的にパッケージ化します。 🎜
<input
    :value="value"
    v-bind="$attrs"
    v-on="listeners"
>

computed: {
  listeners() {
    return {
      ...this.$listeners,
      input: event => 
        this.$emit('input', event.target.value)
    }
  }
}
🎜最後に、main.js に 'components/global.js' をインポートします。これにより、これらの基本コンポーネントを手動で導入することなく、いつでもどこでも使用できるようになります。 🎜🎜3 番目の動き: すべてを最大限に活用するルーター キー 🎜🎜🎜シーン復元: 🎜🎜次のシーンは多くのプログラマーの心を本当に打ち砕きます... まず、デフォルトでは、誰もが制御に Vue-router を使用します。ルーティング。 🎜ブログ Web サイトを作成していて、要件が /post-page/a から /post-page/b にジャンプすることであると仮定します。するとなんとページが飛んだあとデータが更新されていないことが判明? !その理由は、vue-router がこれが同じコンポーネントであることを「賢く」発見し、このコンポーネントを再利用することを決定したため、作成された関数に記述したメソッドがまったく実行されなかったためです。通常の解決策は、次のように $route の変更を監視してデータを初期化することです: 🎜rrreee🎜 バグは解決されましたが、毎回このように書くのはあまりにも洗練されていませんか?できることなら怠けるという原則に従って、コードが次のように記述されることを望みます: 🎜rrreee🎜🎜動き分析:🎜🎜🎜それでは、どうすればそのような効果を達成できるのでしょうか?その答えは、に一意のキーを追加することです。 router-view なので、たとえパブリック コンポーネントであっても、URL が変更されると、このコンポーネントは再作成されます。 (パフォーマンスは少し低下しますが、無限のバグは回避されます)。同時に、キーをルートの完全なパスに直接設定し、一石二鳥であることに注意してください。 🎜rrreee🎜 4 番目のトリック: 全能のレンダリング関数🎜🎜🎜シーン復元:🎜🎜vue では、各コンポーネントにルート要素が 1 つだけ必要です🎜 rrreee🎜🎜移動分析。 :🎜🎜それを解決する方法はありますか? 答えは「はい」ですが、現時点では、テンプレートの代わりに render() 関数を使用して HTML を作成する必要があります。実際、js を使用して HTML を生成する利点は、非常に柔軟で強力であり、v-for や v-if などの Vue の機能が限定された命令 API の使い方を学ぶ必要がないことです。 (reactjs はテンプレートを完全に破棄します)🎜rrreee🎜 5 番目のトリック: トリックなしで勝てる高レベルのコンポーネント🎜🎜キーポイント: このトリックは非常に強力です。 必ずマスターしてください🎜ときコンポーネントを作成します 親コンポーネントと子コンポーネント間の通信は非常に重要です。通常、一連の props を親コンポーネントから子コンポーネントに渡す必要があり、同時に親コンポーネントは子コンポーネントから発行された一連のイベントをリッスンする必要があります。例: 🎜rrreee🎜 には次の最適化ポイントがあります: 🎜🎜1. 親コンポーネントから子コンポーネントに渡されるすべての prop は、使用する前に子コンポーネントの Props で明示的に宣言する必要があります。このように、子コンポーネントは毎回多くの props を宣言する必要がありますが、placeholer のような DOM ネイティブ プロパティについては、実際には宣言せずに親から子に直接渡すことができます。メソッドは次のとおりです。 🎜rrreee🎜$attrs には、props として認識 (および取得) されない親スコープ内の属性バインディング (クラスとスタイルを除く) が含まれています。コンポーネントが props を宣言していない場合、すべての親スコープ バインディングがここに含まれ、内部コンポーネントは v-bind="$attrs" 経由で渡すことができます。これは、上位レベルのコンポーネントを作成するときに非常に便利です。 🎜🎜2. 子コンポーネントの @focus=$emit('focus', $event)" は、実際には何も行わないことに注意してください。これは、イベントを親コンポーネントに返すだけです。上記と同じ 同様に、明示的に宣言する必要はありません: 🎜
<input
    :value="value"
    v-bind="$attrs"
    v-on="listeners"
>

computed: {
  listeners() {
    return {
      ...this.$listeners,
      input: event => 
        this.$emit('input', event.target.value)
    }
  }
}

$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

3.需要注意的是,由于我们input并不是BaseInput这个组件的根节点,而默认情况下父作用域的不被认作 props 的特性绑定将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。所以我们需要设置inheritAttrs:false,这些默认行为将会被去掉, 以上两点的优化才能成功。


结尾

掌握了以上五招,你就能在Vue.js的海洋中自由驰骋了,去吧少年。
陆续可能还会更新一些别的招数,敬请期待。

相关推荐:

Vue.js之CLI框架安装步骤

Vue.js如何实现真分页

Vue.js自定义事件如何进行表单输入组件

以上がVue.js のベスト プラクティス (Vue.js マスターになるための 5 つのヒント)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。