{"id":1320,"date":"2023-03-18T11:59:26","date_gmt":"2023-03-18T03:59:26","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1320"},"modified":"2023-04-29T09:11:38","modified_gmt":"2023-04-29T01:11:38","slug":"taro-beginner-practical-battle","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/18\/taro-beginner-practical-battle\/","title":{"rendered":"Taro\u5165\u95e8\u5b9e\u6218"},"content":{"rendered":"<h2>\u57fa\u7840\u6559\u7a0b<\/h2>\n<p>\u5b89\u88c5\u597d<code>Taro CLI<\/code>\u4e4b\u540e\u53ef\u4ee5\u901a\u8fc7<code>taro init<\/code>\u547d\u4ee4\u521b\u5efa\u4e00\u4e2a\u5168\u65b0\u7684\u9879\u76ee\uff0c\u4e00\u4e2a\u6700\u5c0f\u7248\u672c\u7684<code>Taro<\/code>\u9879\u76ee\u4f1a\u5305\u62ec\u4ee5\u4e0b\u6587\u4ef6\uff1a<\/p>\n<p><!-- more --><\/p>\n<pre><code>\u251c\u2500\u2500 babel.config.js             # Babel \u914d\u7f6e\n\u251c\u2500\u2500 .eslintrc.js                # ESLint \u914d\u7f6e\n\u251c\u2500\u2500 config                      # \u7f16\u8bd1\u914d\u7f6e\u76ee\u5f55\n\u2502   \u251c\u2500\u2500 dev.js                  # \u5f00\u53d1\u6a21\u5f0f\u914d\u7f6e\n\u2502   \u251c\u2500\u2500 index.js                # \u9ed8\u8ba4\u914d\u7f6e\n\u2502   \u2514\u2500\u2500 prod.js                 # \u751f\u4ea7\u6a21\u5f0f\u914d\u7f6e\n\u251c\u2500\u2500 package.json                # Node.js manifest\n\u251c\u2500\u2500 dist                        # \u6253\u5305\u76ee\u5f55\n\u251c\u2500\u2500 project.config.json         # \u5c0f\u7a0b\u5e8f\u9879\u76ee\u914d\u7f6e\n\u251c\u2500\u2500 src # \u6e90\u7801\u76ee\u5f55\n\u2502   \u251c\u2500\u2500 app.config.js           # \u5168\u5c40\u914d\u7f6e\n\u2502   \u251c\u2500\u2500 app.css                 # \u5168\u5c40 CSS\n\u2502   \u251c\u2500\u2500 app.js                  # \u5165\u53e3\u7ec4\u4ef6\n\u2502   \u251c\u2500\u2500 index.html              # H5 \u5165\u53e3 HTML\n\u2502   \u2514\u2500\u2500 pages                   # \u9875\u9762\u7ec4\u4ef6\n\u2502       \u2514\u2500\u2500 index\n\u2502           \u251c\u2500\u2500 index.config.js # \u9875\u9762\u914d\u7f6e\n\u2502           \u251c\u2500\u2500 index.css       # \u9875\u9762 CSS\n\u2502           \u2514\u2500\u2500 index.jsx       # \u9875\u9762\u7ec4\u4ef6\uff0c\u5982\u679c\u662f Vue \u9879\u76ee\uff0c\u6b64\u6587\u4ef6\u4e3a index.vue<\/code><\/pre>\n<p>\u6211\u4eec\u5148\u628a\u6ce8\u610f\u529b\u805a\u7126\u5728<code>src<\/code>\u6587\u4ef6\u5939\uff0c\u4e5f\u5c31\u662f\u6e90\u7801\u76ee\u5f55<\/p>\n<h3>\u5165\u53e3\u7ec4\u4ef6<\/h3>\n<p>\u6bcf\u4e00\u4e2a<code>Taro<\/code>\u9879\u76ee\u90fd\u6709\u4e00\u4e2a\u5165\u53e3\u7ec4\u4ef6\u548c\u4e00\u4e2a\u5165\u53e3\u914d\u7f6e\uff0c\u53ef\u4ee5\u5728\u5165\u53e3\u7ec4\u4ef6\u4e2d\u8bbe\u7f6e\u5168\u5c40\u72b6\u6001\/\u5168\u5c40\u751f\u547d\u5468\u671f\uff0c\u4e00\u4e2a\u6700\u5c0f\u5316\u7684\u5165\u53e3\u7ec4\u4ef6\u4f1a\u662f\u8fd9\u6837\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/app.js<\/code><\/p>\n<pre><code class=\"language-javascript\">import React, { Component } from &#039;react&#039;\nimport &#039;.\/app.css&#039;\n\nclass App extends Component {\n  render () {\n    \/\/ this.props.children \u662f\u5c06\u8981\u4f1a\u6e32\u67d3\u7684\u9875\u9762\n    return this.props.children\n  }\n}\n\n\/\/ \u6bcf\u4e00\u4e2a\u5165\u53e3\u7ec4\u4ef6\u90fd\u5fc5\u987b\u5bfc\u51fa\u4e00\u4e2a React \u7ec4\u4ef6\nexport default App<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/app.js<\/code><\/p>\n<pre><code class=\"language-javascript\">import Vue from &#039;vue&#039;\nimport &#039;.\/app.css&#039;\n\nconst App = new Vue({\n  render(h) {\n    \/\/ this.$slots.default \u662f\u5c06\u8981\u4f1a\u6e32\u67d3\u7684\u9875\u9762\n    return h(&#039;block&#039;, this.$slots.default)\n  }\n})\n\nexport default App<\/code><\/pre>\n<p>\u6bcf\u4e00\u4e2a\u5165\u53e3\u7ec4\u4ef6\uff08\u4f8b\u5982<code>app.js<\/code>\uff09\u603b\u662f\u4f34\u968f\u4e00\u4e2a\u5168\u5c40\u914d\u7f6e\u6587\u4ef6\uff08\u4f8b\u5982<code>app.config.js<\/code>\uff09\uff0c\u6211\u4eec\u53ef\u4ee5\u5728\u5168\u5c40\u914d\u7f6e\u6587\u4ef6\u4e2d\u8bbe\u7f6e\u9875\u9762\u7ec4\u4ef6\u7684\u8def\u5f84\u3001\u5168\u5c40\u7a97\u53e3\u3001\u8def\u7531\u7b49\u4fe1\u606f\uff0c\u4e00\u4e2a\u6700\u7b80\u5355\u7684\u5168\u5c40\u914d\u7f6e\u5982\u4e0b\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code> src\/app.config.js<\/code><\/p>\n<pre><code class=\"language-javascript\">export default {\n  pages: [\n    &#039;pages\/index\/index&#039;\n  ]\n}<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code> src\/app.config.js<\/code><\/p>\n<pre><code class=\"language-javascript\">export default {\n  pages: [\n    &#039;pages\/index\/index&#039;\n  ]\n}<\/code><\/pre>\n<p>\u6ce8\u610f\u5230\uff0c\u4e0d\u7ba1\u662f<code>React<\/code>\u8fd8\u662f<code>Vue<\/code>\uff0c\u4e24\u8005\u7684\u5168\u5c40\u914d\u7f6e\u662f\u4e00\u6837\u7684\u3002\u8fd9\u662f\u5728\u914d\u7f6e\u6587\u4ef6\u4e2d\uff0c<code>Taro<\/code>\u5e76\u4e0d\u5173\u5fc3\u6846\u67b6\u7684\u533a\u522b\uff0c<code>Taro CLI<\/code>\u4f1a\u76f4\u63a5\u5728\u7f16\u8bd1\u65f6\u5728<code>Node.js<\/code>\u73af\u5883\u76f4\u63a5\u6267\u884c\u5168\u5c40\u914d\u7f6e\u7684\u4ee3\u7801\uff0c\u5e76\u628a<code>export default<\/code>\u5bfc\u51fa\u7684\u5bf9\u8c61\u5e8f\u5217\u5316\u4e3a\u4e00\u4e2a<code>JSON<\/code>\u6587\u4ef6\u3002<\/p>\n<h3>\u9875\u9762\u7ec4\u4ef6<\/h3>\n<p>\u9875\u9762\u7ec4\u4ef6\u662f\u6bcf\u4e00\u9879\u8def\u7531\u5c06\u4f1a\u6e32\u67d3\u7684\u9875\u9762\uff0c<code>Taro<\/code>\u7684\u9875\u9762\u9ed8\u8ba4\u653e\u5728<code>src\/pages<\/code>\u4e2d\uff0c\u6bcf\u4e00\u4e2a<code>Taro<\/code>\u9879\u76ee\u81f3\u5c11\u6709\u4e00\u4e2a\u9875\u9762\u7ec4\u4ef6\u3002\u5728\u6211\u4eec\u751f\u6210\u7684\u9879\u76ee\u4e2d\u6709\u4e00\u4e2a\u9875\u9762\u7ec4\u4ef6\uff1a<code>src\/pages\/index\/index<\/code>\uff0c\u53ef\u4ee5\u53d1\u73b0\uff0c\u8fd9\u4e2a\u8def\u5f84\u6070\u5de7\u5bf9\u5e94\u7684\u5c31\u662f\u5168\u5c40\u914d\u7f6e\u7684<code>pages<\/code>\u5b57\u6bb5\u5f53\u4e2d\u7684\u503c\u3002\u4e00\u4e2a\u7b80\u5355\u7684\u9875\u9762\u7ec4\u4ef6\u5982\u4e0b\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/index\/index.jsx<\/code><\/p>\n<pre><code class=\"language-html\">import { View } from &#039;@tarojs\/components&#039;\nclass Index extends Component {\n  state = {\n    msg: &#039;Hello World!&#039;\n  }\n\n  onReady () {\n    console.log(&#039;onReady&#039;)\n  }\n\n  render () {\n    return &lt;View&gt;{ this.state.msg }&lt;\/View&gt;\n  }\n}\n\nexport default Index<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/index\/index.vue<\/code><\/p>\n<pre><code class=\"language-html\">&lt;template&gt;\n  &lt;view&gt;\n    {{ msg }}\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\n\nexport default {\n  data() {\n    return {\n      msg: &#039;Hello World!&#039;\n    };\n  },\n  onReady () {\n    console.log(&#039;onReady&#039;)\n  }\n};\n&lt;\/script&gt;<\/code><\/pre>\n<p>\u8fd9\u4e0d\u6b63\u662f\u6211\u4eec\u719f\u6089\u7684 React \u548c Vue \u7ec4\u4ef6\u5417\uff01\u4f46\u8fd8\u662f\u6709\u4e24\u70b9\u7ec6\u5fae\u7684\u5dee\u522b\uff1a<\/p>\n<ul>\n<li><code>onReady<\/code>\u751f\u547d\u5468\u671f\u51fd\u6570\u3002\u8fd9\u662f\u6765\u6e90\u4e8e\u5fae\u4fe1\u5c0f\u7a0b\u5e8f\u89c4\u8303\u7684\u751f\u547d\u5468\u671f\uff0c\u8868\u793a\u7ec4\u4ef6\u9996\u6b21\u6e32\u67d3\u5b8c\u6bd5\uff0c\u51c6\u5907\u597d\u4e0e\u89c6\u56fe\u4ea4\u4e92\u3002<code>Taro<\/code>\u5728\u8fd0\u884c\u65f6\u5c06\u5927\u90e8\u5206\u5c0f\u7a0b\u5e8f\u89c4\u8303\u9875\u9762\u751f\u547d\u5468\u671f\u6ce8\u5165\u5230\u4e86\u9875\u9762\u7ec4\u4ef6\u4e2d\uff0c\u540c\u65f6<code>React<\/code>\u6216<code>Vue<\/code>\u81ea\u5e26\u7684\u751f\u547d\u5468\u671f\u4e5f\u662f\u5b8c\u5168\u53ef\u4ee5\u6b63\u5e38\u4f7f\u7528\u7684\u3002<\/li>\n<li><code>View<\/code>\u7ec4\u4ef6\u3002\u8fd9\u662f\u6765\u6e90\u4e8e<code>@tarojs\/components<\/code>\u7684\u8de8\u5e73\u53f0\u7ec4\u4ef6\u3002\u76f8\u5bf9\u4e8e\u6211\u4eec\u719f\u6089\u7684<code>div<\/code>\u3001<code>span<\/code>\u5143\u7d20\u800c\u8a00\uff0c\u5728<code>Taro<\/code>\u4e2d\u6211\u4eec\u8981\u5168\u90e8\u4f7f\u7528\u8fd9\u6837\u7684\u8de8\u5e73\u53f0\u7ec4\u4ef6\u8fdb\u884c\u5f00\u53d1\u3002<\/li>\n<\/ul>\n<p>\u548c\u5165\u53e3\u7ec4\u4ef6\u4e00\u6837\uff0c\u6bcf\u4e00\u4e2a\u9875\u9762\u7ec4\u4ef6\uff08\u4f8b\u5982<code>index.vue<\/code>\uff09\u4e5f\u4f1a\u6709\u4e00\u4e2a\u9875\u9762\u914d\u7f6e\uff08\u4f8b\u5982<code>index.config.js<\/code>\uff09\uff0c\u6211\u4eec\u53ef\u4ee5\u5728\u9875\u9762\u914d\u7f6e\u6587\u4ef6\u4e2d\u8bbe\u7f6e\u9875\u9762\u7684\u5bfc\u822a\u680f\u3001\u80cc\u666f\u989c\u8272\u7b49\u53c2\u6570\uff0c\u4e00\u4e2a\u6700\u7b80\u5355\u7684\u9875\u9762\u914d\u7f6e\u5982\u4e0b\uff1a<\/p>\n<ul>\n<li><code>src\/pages\/index\/index.config.js<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-javascript\">export default {\n  navigationBarTitleText: &#039;\u9996\u9875&#039;\n}<\/code><\/pre>\n<blockquote>\n<p><code>Taro<\/code>\u7684\u9875\u9762\u94a9\u5b50\u51fd\u6570\u548c\u9875\u9762\u914d\u7f6e\u89c4\u8303\u662f\u57fa\u4e8e\u5fae\u4fe1\u5c0f\u7a0b\u5e8f\u800c\u5236\u5b9a\u7684\uff0c\u5e76\u5bf9\u5168\u5e73\u53f0\u8fdb\u884c\u7edf\u4e00\u3002\u53ef\u4ee5\u901a\u8fc7\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/react\/#\u9875\u9762\u7ec4\u4ef6\" title=\"React \u9875\u9762\u7ec4\u4ef6\">React \u9875\u9762\u7ec4\u4ef6<\/a> \u548c <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/vue\/#\u9875\u9762\u7ec4\u4ef6\" title=\"Vue \u9875\u9762\u7ec4\u4ef6\">Vue \u9875\u9762\u7ec4\u4ef6<\/a> \u4e86\u89e3\u5168\u90e8\u9875\u9762\u94a9\u5b50\u51fd\u6570\u548c\u9875\u9762\u914d\u7f6e\u89c4\u8303\u3002<\/p>\n<\/blockquote>\n<h3>\u81ea\u5b9a\u4e49\u7ec4\u4ef6<\/h3>\n<p>\u5df2\u7ecf\u7406\u89e3\u4e86<code>Taro<\/code>\u4e2d\u7684\u5165\u53e3\u7ec4\u4ef6\u548c\u9875\u9762\u7ec4\u4ef6\uff0c\u5e76\u4e86\u89e3\u4e86\u5b83\u4eec\u662f\u5982\u4f55\uff08\u901a\u8fc7\u914d\u7f6e\u6587\u4ef6\uff09\u4ea4\u4e92\u7684\u3002\u63a5\u4e0b\u6765\u7684\u5185\u5bb9\uff0c\u5b9e\u73b0\u5982\u4f55\u81ea\u5b9a\u4e49\u7ec4\u4ef6\u3002<\/p>\n<p>\u6211\u4eec\u5148\u628a\u9996\u9875\u5199\u597d\uff0c\u9996\u9875\u7684\u903b\u8f91\u5f88\u7b80\u5355\uff1a\u628a\u8bba\u575b\u6700\u65b0\u7684\u5e16\u5b50\u5c55\u793a\u51fa\u6765\u3002<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/index\/index.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">import Taro from &#039;@tarojs\/taro&#039;\nimport React from &#039;react&#039;\nimport { View } from &#039;@tarojs\/components&#039;\nimport { ThreadList } from &#039;..\/..\/components\/thread_list&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\n\nimport &#039;.\/index.css&#039;\n\nclass Index extends React.Component {\n  config = {\n    navigationBarTitleText: &#039;\u9996\u9875&#039;\n  }\n\n  state = {\n    loading: true,\n    threads: []\n  }\n\n  async componentDidMount () {\n    try {\n      const res = await Taro.request({\n        url: api.getLatestTopic()\n      })\n      this.setState({\n        threads: res.data,\n        loading: false\n      })\n    } catch (error) {\n      Taro.showToast({\n        title: &#039;\u8f7d\u5165\u8fdc\u7a0b\u6570\u636e\u9519\u8bef&#039;\n      })\n    }\n  }\n\n  render () {\n    const { loading, threads } = this.state\n    return (\n      &lt;View className=&#039;index&#039;&gt;\n        &lt;ThreadList\n          threads={threads}\n          loading={loading}\n        \/&gt;\n      &lt;\/View&gt;\n    )\n  }\n}\n\nexport default Index<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/index\/index.vue<\/code><\/p>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;view class=&#039;index&#039;&gt;\n    &lt;thread-list\n      :threads=&quot;threads&quot;\n      :loading=&quot;loading&quot;\n    \/&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport Vue from &#039;vue&#039;\nimport Taro from &#039;@tarojs\/taro&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\nimport ThreadList from &#039;..\/..\/components\/thread_list.vue&#039;\nexport default {\n  components: {\n    &#039;thread-list&#039;: ThreadList\n  },\n  data () {\n    return {\n      loading: true,\n      threads: []\n    }\n  },\n  async created() {\n    try {\n      const res = await Taro.request({\n        url: api.getLatestTopic()\n      })\n      this.loading = false\n      this.threads = res.data\n    } catch (error) {\n      Taro.showToast({\n        title: &#039;\u8f7d\u5165\u8fdc\u7a0b\u6570\u636e\u9519\u8bef&#039;\n      })\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<blockquote>\n<p>\u6ce8\u610f\u5230\u5728\u4e00\u4e2a<code>Taro<\/code>\u5e94\u7528\u4e2d\u53d1\u9001\u8bf7\u6c42\u662f<code>Taro.request()<\/code>\u5b8c\u6210\u7684\u3002\u548c\u9875\u9762\u914d\u7f6e\u3001\u5168\u5c40\u914d\u7f6e\u4e00\u6837\uff0c<code>Taro<\/code>\u7684 API\u89c4\u8303\u4e5f\u662f\u57fa\u4e8e\u5fae\u4fe1\u5c0f\u7a0b\u5e8f\u800c\u5236\u5b9a\u7684\uff0c\u5e76\u5bf9\u5168\u5e73\u53f0\u8fdb\u884c\u7edf\u4e00\u3002\u53ef\u4ee5\u901a\u8fc7\u5728 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/apis\/about\/desc\" title=\"API \u6587\u6863\">API \u6587\u6863<\/a> \u627e\u5230\u6240\u6709 API\u3002<\/p>\n<\/blockquote>\n<p>\u5728\u6211\u4eec\u7684\u9996\u9875\u7ec4\u4ef6\u91cc\uff0c\u8fd8\u5f15\u7528\u4e86\u4e00\u4e2a<code>ThreadList<\/code>\u7ec4\u4ef6\uff0c\u6211\u4eec\u73b0\u5728\u6765\u5b9e\u73b0\u5b83\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/components\/thread_list.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">import React from &#039;react&#039;\nimport { View, Text } from &#039;@tarojs\/components&#039;\nimport { Thread } from &#039;.\/thread&#039;\nimport { Loading } from &#039;.\/loading&#039;\n\nimport &#039;.\/thread.css&#039;\n\nclass ThreadList extends React.Component {\n  static defaultProps = {\n    threads: [],\n    loading: true\n  }\n\n  render () {\n    const { loading, threads } = this.props\n\n    if (loading) {\n      return &lt;Loading \/&gt;\n    }\n\n    const element = threads.map((thread, index) =&gt; {\n      return (\n        &lt;Thread\n          key={thread.id}\n          node={thread.node}\n          title={thread.title}\n          last_modified={thread.last_modified}\n          replies={thread.replies}\n          tid={thread.id}\n          member={thread.member}\n        \/&gt;\n      )\n    })\n\n    return (\n      &lt;View className=&#039;thread-list&#039;&gt;\n        {element}\n      &lt;\/View&gt;\n    )\n  }\n}\n\nexport { ThreadList }<\/code><\/pre>\n<p>React\u7248\u672c\uff1a<code>src\/components\/thread.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">import Taro, { eventCenter } from &#039;@tarojs\/taro&#039;\nimport React from &#039;react&#039;\nimport { View, Text, Navigator, Image } from &#039;@tarojs\/components&#039;\n\nimport api from &#039;..\/utils\/api&#039;\nimport { timeagoInst, Thread_DETAIL_NAVIGATE } from &#039;..\/utils&#039;\n\nclass Thread extends React.Component {\n\n  handleNavigate = () =&gt; {\n    const { tid, not_navi } = this.props\n    if (not_navi) {\n      return\n    }\n    eventCenter.trigger(Thread_DETAIL_NAVIGATE, this.props)\n    \/\/ \u8df3\u8f6c\u5230\u5e16\u5b50\u8be6\u60c5\n    Taro.navigateTo({\n      url: &#039;\/pages\/thread_detail\/thread_detail&#039;\n    })\n  }\n\n  render () {\n    const { title, member, last_modified, replies, node, not_navi } = this.props\n    const time = timeagoInst.format(last_modified * 1000, &#039;zh&#039;)\n    const usernameCls = `author ${not_navi ? &#039;bold&#039; : &#039;&#039;}`\n\n    return (\n      &lt;View className=&#039;thread&#039; onClick={this.handleNavigate}&gt;\n        &lt;View className=&#039;info&#039;&gt;\n          &lt;View&gt;\n            &lt;Image src={member.avatar_large} className=&#039;avatar&#039; \/&gt;\n          &lt;\/View&gt;\n          &lt;View className=&#039;middle&#039;&gt;\n            &lt;View className={usernameCls}&gt;\n                {member.username}\n            &lt;\/View&gt;\n            &lt;View className=&#039;replies&#039;&gt;\n              &lt;Text className=&#039;mr10&#039;&gt;\n                {time}\n              &lt;\/Text&gt;\n              &lt;Text&gt;\n                \u8bc4\u8bba {replies}\n              &lt;\/Text&gt;\n            &lt;\/View&gt;\n          &lt;\/View&gt;\n          &lt;View className=&#039;node&#039;&gt;\n            &lt;Text className=&#039;tag&#039;&gt;\n              {node.title}\n            &lt;\/Text&gt;\n          &lt;\/View&gt;\n        &lt;\/View&gt;\n        &lt;Text className=&#039;title&#039;&gt;\n          {title}\n        &lt;\/Text&gt;\n      &lt;\/View&gt;\n    )\n  }\n}\n\nexport { Thread }<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/components\/thread_list.vue<\/code><\/p>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;view className=&#039;thread-list&#039;&gt;\n    &lt;loading v-if=&quot;loading&quot; \/&gt;\n    &lt;thread\n      v-else\n      v-for=&quot;t in threads&quot;\n      :key=&quot;t.id&quot;\n      :node=&quot;t.node&quot;\n      :title=&quot;t.title&quot;\n      :last_modified=&quot;t.last_modified&quot;\n      :replies=&quot;t.replies&quot;\n      :tid=&quot;t.id&quot;\n      :member=&quot;t.member&quot;\n    \/&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script &gt;\nimport Vue from &#039;vue&#039;\nimport Loading from &#039;.\/loading.vue&#039;\nimport Thread from &#039;.\/thread.vue&#039;\nexport default {\n  components: {\n    &#039;loading&#039;: Loading,\n    &#039;thread&#039;: Thread\n  },\n  props: {\n    threads: {\n      type: Array,\n      default: []\n    },\n    loading: {\n      type: Boolean,\n      default: true\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/components\/thread.vue<\/code><\/p>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;view class=&#039;thread&#039; @tap=&quot;handleNavigate&quot;&gt;\n    &lt;view class=&#039;info&#039;&gt;\n      &lt;view&gt;\n        &lt;image :src=&quot;member.avatar_large | url&quot; class=&#039;avatar&#039; \/&gt;\n      &lt;\/view&gt;\n      &lt;view class=&#039;middle&#039;&gt;\n        &lt;view :class=&quot;usernameCls&quot;&gt;\n            {{member.username}}\n        &lt;\/view&gt;\n        &lt;view class=&#039;replies&#039;&gt;\n          &lt;text class=&#039;mr10&#039;&gt;{{time}}&lt;\/text&gt;\n          &lt;text&gt;\u8bc4\u8bba {{replies}}&lt;\/text&gt;\n        &lt;\/view&gt;\n      &lt;\/view&gt;\n      &lt;view class=&#039;node&#039;&gt;\n        &lt;text class=&#039;tag&#039;&gt;{{node.title}}&lt;\/Text&gt;\n      &lt;\/view&gt;\n    &lt;\/view&gt;\n    &lt;text class=&#039;title&#039;&gt;{{title}}&lt;\/text&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport Vue from &#039;vue&#039;\nimport { eventCenter } from &#039;@tarojs\/taro&#039;\nimport Taro from &#039;@tarojs\/taro&#039;\nimport { timeagoInst, Thread_DETAIL_NAVIGATE } from &#039;..\/utils&#039;\nimport &#039;.\/thread.css&#039;\nexport default {\n  props: [&#039;title&#039;, &#039;member&#039;, &#039;last_modified&#039;, &#039;replies&#039;, &#039;node&#039;, &#039;not_navi&#039;, &#039;tid&#039;],\n  computed: {\n    time () {\n      return timeagoInst.format(this.last_modified * 1000, &#039;zh&#039;)\n    },\n    usernameCls () {\n      return `author ${this.not_navi ? &#039;bold&#039; : &#039;&#039;}`\n    }\n  },\n  filters: {\n    url (val) {\n      return &#039;https:&#039; + val\n    }\n  },\n  methods: {\n    handleNavigate () {\n      const { tid, not_navi } = this.$props\n      if (not_navi) {\n        return\n      }\n      eventCenter.trigger(Thread_DETAIL_NAVIGATE, this.$props)\n      \/\/ \u8df3\u8f6c\u5230\u5e16\u5b50\u8be6\u60c5\n      Taro.navigateTo({\n        url: &#039;\/pages\/thread_detail\/thread_detail&#039;\n      })\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<p>\u8fd9\u91cc\u53ef\u4ee5\u53d1\u73b0\u6211\u4eec\u628a\u8bba\u575b\u5e16\u5b50\u6e32\u67d3\u903b\u8f91\u62c6\u6210\u4e86\u4e24\u4e2a\u7ec4\u4ef6\uff0c\u5e76\u653e\u5728<code>src\/components<\/code>\u6587\u4ef6\u4e2d\uff0c\u56e0\u4e3a\u8fd9\u4e9b\u7ec4\u4ef6\u662f\u4f1a\u5728\u5176\u5b83\u9875\u9762\u4e2d\u591a\u6b21\u7528\u5230\u3002\u62c6\u5206\u7ec4\u4ef6\u7684\u529b\u5ea6\u662f\u5b8c\u5168\u7531\u5f00\u53d1\u8005\u51b3\u5b9a\u7684\uff0c<code>Taro<\/code>\u5e76\u6ca1\u6709\u89c4\u5b9a\u7ec4\u4ef6\u4e00\u5b9a\u8981\u653e\u5728<code>components<\/code>\u6587\u4ef6\u5939\uff0c\u4e5f\u6ca1\u6709\u89c4\u5b9a\u9875\u9762\u4e00\u5b9a\u8981\u653e\u5728<code>pages<\/code>\u6587\u4ef6\u5939\u3002<\/p>\n<p>\u53e6\u5916\u4e00\u4e2a\u503c\u5f97\u6ce8\u610f\u7684\u70b9\u662f\uff1a\u6211\u4eec\u5e76\u6ca1\u6709\u4f7f\u7528<code>div\/span<\/code>\u8fd9\u6837\u7684 HTML \u7ec4\u4ef6\uff0c\u800c\u662f\u4f7f\u7528\u4e86<code>View\/Text<\/code>\u8fd9\u6837\u7684\u8de8\u5e73\u53f0\u7ec4\u4ef6\u3002<\/p>\n<blockquote>\n<p><code>Taro<\/code>\u6587\u6863\u7684\u8de8\u5e73\u53f0\u7ec4\u4ef6\u5e93 \u5305\u542b\u4e86\u6240\u6709\u7ec4\u4ef6\u53c2\u6570\u548c\u7528\u6cd5\u3002\u4f46\u76ee\u524d\u7ec4\u4ef6\u5e93\u6587\u6863\u4e2d\u7684\u53c2\u6570\u548c\u7ec4\u4ef6\u540d\u90fd\u662f\u9488\u5bf9 React \u7684\uff08\u9664\u4e86<code>React<\/code>\u7684\u70b9\u51fb\u4e8b\u4ef6\u662f<code>onClick<\/code>\u4e4b\u5916\uff09\u3002 \u5bf9\u4e8e Vue \u800c\u8a00\uff0c\u7ec4\u4ef6\u540d\u548c\u7ec4\u4ef6\u53c2\u6570\u90fd\u91c7\u7528\u77ed\u6a2a\u7ebf\u98ce\u683c\uff08<code>kebab-case<\/code>\uff09\u7684\u547d\u540d\u65b9\u5f0f\uff0c\u4f8b\u5982\uff1a<code>&lt;picker-view indicator-class=&quot;myclass&quot; \/&gt;<\/code><\/p>\n<\/blockquote>\n<h3>\u8def\u7531\u4e0e Tabbar<\/h3>\n<p>\u5728<code>src\/components\/thread<\/code>\u7ec4\u4ef6\u4e2d\uff0c\u6211\u4eec\u901a\u8fc7<\/p>\n<pre><code class=\"language-javascript\">Taro.navigateTo({ url: &#039;\/pages\/thread_detail\/thread_detail&#039; })<\/code><\/pre>\n<p>\u8df3\u8f6c\u5230\u5e16\u5b50\u8be6\u60c5\uff0c\u4f46\u8fd9\u4e2a\u9875\u9762\u4ecd\u672a\u5b9e\u73b0\uff0c\u73b0\u5728\u6211\u4eec\u53bb\u5165\u53e3\u6587\u4ef6\u914d\u7f6e\u4e00\u4e2a\u65b0\u7684\u9875\u9762<code>src\/app.config.js<\/code>\uff1a<\/p>\n<pre><code class=\"language-javascript\">export default {\n  pages: [\n    &#039;pages\/index\/index&#039;,\n    &#039;pages\/thread_detail\/thread_detail&#039;\n  ]\n}<\/code><\/pre>\n<p>\u7136\u540e\u5728\u8def\u5f84<code>src\/pages\/thread_detail\/thread_detail<\/code>\u5b9e\u73b0\u5e16\u5b50\u8be6\u60c5\u9875\u9762\uff0c\u8def\u7531\u5c31\u53ef\u4ee5\u8df3\u8f6c\uff0c\u6211\u4eec\u6574\u4e2a\u6d41\u7a0b\u5c31\u8dd1\u8d77\u6765\u4e86\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.jsx<\/code><\/p>\n<pre><code class=\"language-html\">import Taro from &#039;@tarojs\/taro&#039;\nimport React from &#039;react&#039;\nimport { View, RichText, Image } from &#039;@tarojs\/components&#039;\nimport { Thread } from &#039;..\/..\/components\/thread&#039;\nimport { Loading } from &#039;..\/..\/components\/loading&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\nimport { timeagoInst, GlobalState } from &#039;..\/..\/utils&#039;\n\nimport &#039;.\/index.css&#039;\n\nfunction prettyHTML (str) {\n  const lines = [&#039;p&#039;, &#039;h1&#039;, &#039;h2&#039;, &#039;h3&#039;, &#039;h4&#039;, &#039;h5&#039;, &#039;h6&#039;]\n\n  lines.forEach(line =&gt; {\n    const regex = new RegExp(`&lt;${line}`, &#039;gi&#039;)\n\n    str = str.replace(regex, `&lt;${line} class=&quot;line&quot;`)\n  })\n\n  return str.replace(\/&lt;img\/gi, &#039;&lt;img class=&quot;img&quot;&#039;)\n}\n\nclass ThreadDetail extends React.Component {\n  state = {\n    loading: true,\n    replies: [],\n    content: &#039;&#039;,\n    thread: {}\n  } as IState\n\n  config = {\n    navigationBarTitleText: &#039;\u8bdd\u9898&#039;\n  }\n\n  componentWillMount () {\n    this.setState({\n      thread: GlobalState.thread\n    })\n  }\n\n  async componentDidMount () {\n    try {\n      const id = GlobalState.thread.tid\n      const [{ data }, { data: [ { content_rendered } ] } ] = await Promise.all([\n        Taro.request({\n          url: api.getReplies({\n            &#039;topic_id&#039;: id\n          })\n        }),\n        Taro.request({\n          url: api.getTopics({\n            id\n          })\n        })\n      ])\n      this.setState({\n        loading: false,\n        replies: data,\n        content: prettyHTML(content_rendered)\n      })\n    } catch (error) {\n      Taro.showToast({\n        title: &#039;\u8f7d\u5165\u8fdc\u7a0b\u6570\u636e\u9519\u8bef&#039;\n      })\n    }\n  }\n\n  render () {\n    const { loading, replies, thread, content } = this.state\n\n    const replieEl = replies.map((reply, index) =&gt; {\n      const time = timeagoInst.format(reply.last_modified * 1000, &#039;zh&#039;)\n      return (\n        &lt;View className=&#039;reply&#039; key={reply.id}&gt;\n          &lt;Image src={reply.member.avatar_large} className=&#039;avatar&#039; \/&gt;\n          &lt;View className=&#039;main&#039;&gt;\n            &lt;View className=&#039;author&#039;&gt;\n              {reply.member.username}\n            &lt;\/View&gt;\n            &lt;View className=&#039;time&#039;&gt;\n              {time}\n            &lt;\/View&gt;\n            &lt;RichText nodes={reply.content} className=&#039;content&#039; \/&gt;\n            &lt;View className=&#039;floor&#039;&gt;\n              {index + 1} \u697c\n            &lt;\/View&gt;\n          &lt;\/View&gt;\n        &lt;\/View&gt;\n      )\n    })\n\n    const contentEl = loading\n      ? &lt;Loading \/&gt;\n      : (\n        &lt;View&gt;\n          &lt;View className=&#039;main-content&#039;&gt;\n          &lt;RichText nodes={content} \/&gt;\n          &lt;\/View&gt;\n          &lt;View className=&#039;replies&#039;&gt;\n            {replieEl}\n          &lt;\/View&gt;\n        &lt;\/View&gt;\n      )\n\n    return (\n      &lt;View className=&#039;detail&#039;&gt;\n        &lt;Thread\n          node={thread.node}\n          title={thread.title}\n          last_modified={thread.last_modified}\n          replies={thread.replies}\n          tid={thread.id}\n          member={thread.member}\n          not_navi={true}\n        \/&gt;\n        {contentEl}\n      &lt;\/View&gt;\n    )\n  }\n}\n\nexport default ThreadDetail<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.vue<\/code><\/p>\n<pre><code class=\"language-html\">&lt;template&gt;\n  &lt;view class=&#039;detail&#039;&gt;\n    &lt;thread\n      :node=&quot;topic.node&quot;\n      :title=&quot;topic.title&quot;\n      :last_modified=&quot;topic.last_modified&quot;\n      :replies=&quot;topic.replies&quot;\n      :tid=&quot;topic.id&quot;\n      :member=&quot;topic.member&quot;\n      :not_navi=&quot;true&quot;\n    \/&gt;\n    &lt;loading v-if=&quot;loading&quot; \/&gt;\n    &lt;view v-else&gt;\n      &lt;view class=&#039;main-content&#039;&gt;\n        &lt;rich-text :nodes=&quot;content | html&quot; \/&gt;\n      &lt;\/view&gt;\n      &lt;view class=&#039;replies&#039;&gt;\n        &lt;view v-for=&quot;(reply, index) in replies&quot; class=&#039;reply&#039; :key=&quot;reply.id&quot;&gt;\n          &lt;image :src=&#039;reply.member.avatar_large&#039; class=&#039;avatar&#039; \/&gt;\n          &lt;view class=&#039;main&#039;&gt;\n            &lt;view class=&#039;author&#039;&gt;\n              {{reply.member.username}}\n            &lt;\/view&gt;\n            &lt;view class=&#039;time&#039;&gt;\n              {{reply.last_modified | time}}\n            &lt;\/view&gt;\n            &lt;rich-text :nodes=&quot;reply.content_rendered | html&quot; class=&#039;content&#039; \/&gt;\n            &lt;view class=&#039;floor&#039;&gt;\n              {{index + 1}} \u697c\n            &lt;\/view&gt;\n          &lt;\/view&gt;\n        &lt;\/view&gt;\n      &lt;\/view&gt;\n    &lt;\/view&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport Vue from &#039;vue&#039;\nimport Taro from &#039;@tarojs\/taro&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\nimport { timeagoInst, GlobalState, IThreadProps, prettyHTML } from &#039;..\/..\/utils&#039;\nimport Thread from &#039;..\/..\/components\/thread.vue&#039;\nimport Loading from &#039;..\/..\/components\/loading.vue&#039;\nimport &#039;.\/index.css&#039;\nexport default {\n  components: {\n    &#039;loading&#039;: Loading,\n    &#039;thread&#039;: Thread\n  },\n  data () {\n    return {\n      topic: GlobalState.thread,\n      loading: true,\n      replies: [],\n      content: &#039;&#039;\n    }\n  },\n  async created () {\n    try {\n      const id = GlobalState.thread.tid\n      const [{ data }, { data: [ { content_rendered } ] } ] = await Promise.all([\n        Taro.request({\n          url: api.getReplies({\n            &#039;topic_id&#039;: id\n          })\n        }),\n        Taro.request({\n          url: api.getTopics({\n            id\n          })\n        })\n      ])\n      this.loading = false\n      this.replies = data\n      this.content = content_rendered\n    } catch (error) {\n      Taro.showToast({\n        title: &#039;\u8f7d\u5165\u8fdc\u7a0b\u6570\u636e\u9519\u8bef&#039;\n      })\n    }\n  },\n  filters: {\n    time (val) {\n      return timeagoInst.format(val * 1000)\n    },\n    html (val) {\n      return prettyHTML(val)\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u5df2\u7ecf\u5b9e\u73b0\u4e86\u8fd9\u4e2a\u5e94\u7528\u7684\u6240\u6709\u903b\u8f91\uff0c\u9664\u53bb\u300c\u8282\u70b9\u5217\u8868\u300d\u9875\u9762\uff08\u5728\u8fdb\u9636\u6307\u5357\u6211\u4eec\u4f1a\u8ba8\u8bba\u8fd9\u4e2a\u9875\u9762\u7ec4\u4ef6\uff09\u4e4b\u5916\uff0c\u5269\u4e0b\u7684\u9875\u9762\u90fd\u53ef\u4ee5\u901a\u8fc7\u6211\u4eec\u5df2\u7ecf\u8bb2\u89e3\u8fc7\u7684\u7ec4\u4ef6\u6216\u9875\u9762\u5feb\u901f\u62bd\u8c61\u5b8c\u6210\u3002\u6309\u7167\u6211\u4eec\u7684\u8ba1\u5212\uff0c\u8fd9\u4e2a\u5e94\u7528\u4f1a\u6709\u4e94\u4e2a\u9875\u9762\uff0c\u5206\u522b\u662f\uff1a<\/p>\n<ul>\n<li>\u9996\u9875\uff0c\u5c55\u793a\u6700\u65b0\u5e16\u5b50\uff08\u5df2\u5b8c\u6210\uff09<\/li>\n<li>\u8282\u70b9\u5217\u8868<\/li>\n<li>\u70ed\u95e8\u5e16\u5b50\uff08\u53ef\u901a\u8fc7\u7ec4\u4ef6\u590d\u7528\uff09<\/li>\n<li>\u8282\u70b9\u5e16\u5b50\uff08\u53ef\u901a\u8fc7\u7ec4\u4ef6\u590d\u7528\uff09<\/li>\n<li>\u5e16\u5b50\u8be6\u60c5\uff08\u5df2\u5b8c\u6210\uff09<\/li>\n<\/ul>\n<p>\u5176\u4e2d\u524d\u4e09\u4e2a\u9875\u9762\u6211\u4eec\u53ef\u4ee5\u628a\u5b83\u4eec\u89c4\u5212\u5728<code>tabBar<\/code>\u91cc\uff0c<code>tabBar<\/code>\u662f<code>Taro<\/code>\u5185\u7f6e\u7684\u5bfc\u822a\u680f\uff0c\u53ef\u4ee5\u5728<code>app.config.js<\/code>\u914d\u7f6e\uff0c\u914d\u7f6e\u5b8c\u6210\u4e4b\u540e\u5904\u4e8e<code>tabBar<\/code>\u4f4d\u7f6e\u7684\u9875\u9762\u4f1a\u663e\u793a\u4e00\u4e2a\u5bfc\u822a\u680f\u3002\u6700\u7ec8\u6211\u4eec\u7684<code>app.config.js<\/code>\u4f1a\u662f\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"language-javascript\">export default {\n  pages: [\n    &#039;pages\/index\/index&#039;,\n    &#039;pages\/nodes\/nodes&#039;,\n    &#039;pages\/hot\/hot&#039;,\n    &#039;pages\/node_detail\/node_detail&#039;,\n    &#039;pages\/thread_detail\/thread_detail&#039;\n  ],\n  tabBar: {\n    list: [{\n      &#039;iconPath&#039;: &#039;resource\/latest.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/lastest_on.png&#039;,\n      pagePath: &#039;pages\/index\/index&#039;,\n      text: &#039;\u6700\u65b0&#039;\n    }, {\n      &#039;iconPath&#039;: &#039;resource\/hotest.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/hotest_on.png&#039;,\n      pagePath: &#039;pages\/hot\/hot&#039;,\n      text: &#039;\u70ed\u95e8&#039;\n    }, {\n      &#039;iconPath&#039;: &#039;resource\/node.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/node_on.png&#039;,\n      pagePath: &#039;pages\/nodes\/nodes&#039;,\n      text: &#039;\u8282\u70b9&#039;\n    }],\n    &#039;color&#039;: &#039;#000&#039;,\n    &#039;selectedColor&#039;: &#039;#56abe4&#039;,\n    &#039;backgroundColor&#039;: &#039;#fff&#039;,\n    &#039;borderStyle&#039;: &#039;white&#039;\n  },\n  window: {\n    backgroundTextStyle: &#039;light&#039;,\n    navigationBarBackgroundColor: &#039;#fff&#039;,\n    navigationBarTitleText: &#039;V2EX&#039;,\n    navigationBarTextStyle: &#039;black&#039;\n  }\n}<\/code><\/pre>\n<h2>\u9879\u76ee\u8fdb\u9636\u4e0e\u4f18\u5316<\/h2>\n<h3>\u72b6\u6001\u7ba1\u7406<\/h3>\n<p>\u5728\u6211\u4eec\u5b9e\u73b0\u5e16\u5b50\u7ec4\u4ef6\uff08<code>src\/components\/thread<\/code>\uff09\u65f6\uff0c\u901a\u8fc7<code>Taro<\/code>\u5185\u7f6e\u7684<code>eventCenter<\/code>\u53d1\u8d77\u4e86\u4e00\u4e2a\u4e8b\u4ef6\uff0c\u628a\u5f53\u524d\u5e16\u5b50\u7684\u6570\u636e\u6ce8\u5165\u5230\u4e00\u4e2a\u5168\u5c40\u7684<code>GlobalState<\/code>\u4e2d\uff0c\u7136\u540e\u5728\u5e16\u5b50\u8be6\u60c5\u9875\u9762\u518d\u4ece<code>GlobalState<\/code>\u53d6\u51fa\u5f53\u524d\u5e16\u5b50\u7684\u6570\u636e\u2014\u2014\u8fd9\u79cd\u7b80\u5355\u7684\u53d1\u5e03\/\u8ba2\u9605\u6a21\u5f0f\u5728\u5904\u7406\u7b80\u5355\u903b\u8f91\u65f6\u975e\u5e38\u6709\u6548\u4e14\u6e05\u6670\u3002<\/p>\n<p>\u4e00\u65e6\u6211\u4eec\u7684\u4e1a\u52a1\u903b\u8f91\u53d8\u5f97\u590d\u6742\uff0c\u4e00\u4e2a\u7b80\u5355\u7684\u53d1\u5e03\u8ba2\u9605\u673a\u5236\u7ed1\u5b9a\u5230\u4e00\u4e2a\u5168\u5c40\u7684<code>state<\/code>\u53ef\u80fd\u5c31\u4f1a\u5bfc\u81f4\u6211\u4eec\u7684\u6570\u636e\u6d41\u53d8\u5f97\u96be\u4ee5\u8ffd\u8e2a\u3002\u597d\u5728\u8fd9\u4e2a\u95ee\u9898\u4e0d\u7ba1\u662f\u5728<code>React<\/code>\u8fd8\u662f<code>Vue<\/code>\u793e\u533a\u4e2d\u90fd\u6709\u5f88\u597d\u7684\u89e3\u51b3\u65b9\u6848\u3002\u6211\u4eec\u4f1a\u4f7f\u7528\u8fd9\u4e24\u4e2a\u793e\u533a\u6700\u70ed\u95e8\u7684\u72b6\u6001\u7ba1\u7406\u5de5\u5177\uff1a<code>Redux<\/code>\u548c<code>Vuex<\/code>\u6765\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\u3002<\/p>\n<h4>Redux<\/h4>\n<p>\u9996\u5148\u5b89\u88c5<code>redux<\/code>\u548c<code>react-redux<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">npm i redux react-redux<\/code><\/pre>\n<p>\u5728\u5165\u53e3\u6587\u4ef6\u4f7f\u7528<code>react-redux<\/code>\u7684<code>Provider<\/code>\u6ce8\u5165<code>context<\/code>\u5230\u6211\u4eec\u7684\u5e94\u7528\uff1a<\/p>\n<p>src\/app.js<\/p>\n<pre><code class=\"language-javascript\">import React, { Component } from &#039;react&#039;\nimport { Provider } from &#039;react-redux&#039;\nimport { createStore, combineReducers } from &#039;redux&#039;;\nimport &#039;.\/app.css&#039;\n\nconst reducers = combineReducers({\n  thread: (state = {}, action) =&gt; {\n    if (action.type === &#039;SET_CURRENT_THREAD&#039;) {\n      return {\n        ...state,\n        ...action.thread\n      }\n    }\n    return state\n  }\n})\n\nconst store = createStore(reducers)\n\nclass App extends Component {\n  render () {\n    \/\/ this.props.children \u662f\u5c06\u8981\u4f1a\u6e32\u67d3\u7684\u9875\u9762\n    return (\n      &lt;Provider store={store}&gt;\n        {this.props.children}\n      &lt;\/Provider&gt;\n    )\n  }\n}\n\nexport default App<\/code><\/pre>\n<p>\u7136\u540e\u5728\u5e16\u5b50\u7ec4\u4ef6\u4e2d\u6211\u4eec\u5c31\u53ef\u4ee5\u901a\u8fc7<code>connect<\/code>\u4e00\u4e2a<code>dispatch<\/code>\u8bbe\u7f6e\u5f53\u524d\u7684\u5e16\u5b50\uff1a<\/p>\n<p>src\/components\/thread.jsx<\/p>\n<pre><code class=\"language-javascript\">- eventCenter.trigger(Thread_DETAIL_NAVIGATE, this.props)\n+ this.props.setThread(this.props)\n- export default Thread\n+ const mapDispatchToProps = dispatch =&gt; {\n+   return {\n+     setThread: thread =&gt; dispatch({ type: &#039;SET_CURRENT_THREAD&#039;, thread })\n+   }\n+ }\n+ export default connect(null, mapDispatchToProps)(Thread)<\/code><\/pre>\n<p>\u5728\u5e16\u5b50\u8be6\u60c5\u7ec4\u4ef6\u4e2d\u901a\u8fc7<code>connect<\/code>\u4e00\u4e2a<code>mapStateToProps<\/code>\u83b7\u53d6\u5f53\u524d\u5e16\u5b50\u7684\u6570\u636e\uff1a<\/p>\n<p>src\/components\/thread_detail.jsx<\/p>\n<pre><code class=\"language-javascript\">- const id = GlobalState.thread.tid\n+ const id = this.props.thread.tid\n- export default ThreadDetail\n+ function mapStateToProps(state) {\n+  return { thread: state.thread }\n+ }\n+ export default connect(mapStateToProps)(ThreadDetail)<\/code><\/pre>\n<blockquote>\n<p>\u6b64\u6559\u7a0b\u6f14\u793a\u7684\u662f<code>Redux<\/code>\u6781\u7b80\u7528\u6cd5\uff0c\u800c\u975e\u6700\u4f73\u5b9e\u8df5\u3002\u8be6\u60c5\u8bf7\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.redux.org.cn\" title=\"Redux \u6587\u6863\">Redux \u6587\u6863<\/a> \u548c <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/react-redux.js.org\/introduction\/quick-start\" title=\"react-redux \u6587\u6863\">react-redux \u6587\u6863<\/a>\u3002<\/p>\n<\/blockquote>\n<h4>Vuex<\/h4>\n<p>\u9996\u5148\u5b89\u88c5<code>vuex<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">npm i vuex<\/code><\/pre>\n<p>\u5728\u5165\u53e3\u6587\u4ef6\u4e2d\u6ce8\u5165 Vuex \u7684<code>store<\/code>\uff1a<\/p>\n<pre><code class=\"language-javascript\">src\/app.js\nimport Vue from &#039;vue&#039;\nimport &#039;.\/app.css&#039;\n\nconst store = new Vuex.Store({\n  state: {\n    thread: {}\n  },\n  mutations: {\n    setThread: (state, thread) =&gt; {\n      state.thread = { ...thread }\n    }\n  }\n})\n\nconst App = new Vue({\n  store,\n  render(h) {\n    \/\/ this.$slots.default \u662f\u5c06\u8981\u4f1a\u6e32\u67d3\u7684\u9875\u9762\n    return h(&#039;block&#039;, this.$slots.default)\n  }\n})\n\nexport default App<\/code><\/pre>\n<p>\u7136\u540e\u5728\u5e16\u5b50\u7ec4\u4ef6\u4e2d\u6211\u4eec\u5c31\u53ef\u4ee5\u901a\u8fc7<code>this.$store.setThread()<\/code>\u8bbe\u7f6e\u5f53\u524d\u7684\u5e16\u5b50\uff1a<\/p>\n<p>src\/components\/thread.vue<\/p>\n<pre><code class=\"language-javascript\">- eventCenter.trigger(Thread_DETAIL_NAVIGATE, this.props)\n+ this.$store.setThread(this.$props)<\/code><\/pre>\n<p>\u5728\u5e16\u5b50\u8be6\u60c5\u7ec4\u4ef6\u4e2d\u901a\u8fc7 computed \u83b7\u53d6\u5f53\u524d\u5e16\u5b50\u7684\u6570\u636e\uff1a<\/p>\n<p>src\/components\/thread_detail.vue<\/p>\n<pre><code class=\"language-javascript\">{\n  data () {\n    return {\n-     topic: GlobalState.thread,\n      loading: true,\n      replies: [],\n      content: &#039;&#039;\n    }\n  },\n+ computed: {\n+  topic() {\n+    return this.$store.state.thread\n+  }\n+ }\n}<\/code><\/pre>\n<blockquote>\n<p>\u6b64\u6559\u7a0b\u6f14\u793a\u7684\u662f Vuex \u6781\u7b80\u7528\u6cd5\uff0c\u800c\u975e\u6700\u4f73\u5b9e\u8df5\u3002\u8be6\u60c5\u8bf7\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/vuex.vuejs.org\/zh\/guide\/\" title=\"Vuex \u6587\u6863\">Vuex \u6587\u6863<\/a>\u3002<\/p>\n<\/blockquote>\n<h4>\u5176\u5b83\u72b6\u6001\u7ba1\u7406\u5de5\u5177<\/h4>\n<p>\u539f\u7406\u4e0a\u6765\u8bf4\uff0c<code>Taro<\/code>\u53ef\u4ee5\u652f\u6301\u4efb\u4f55\u517c\u5bb9 React \u6216 Vue \u7684\u72b6\u6001\u7ba1\u7406\u5de5\u5177\uff0c\u4f7f\u7528\u8fd9\u7c7b\u5de5\u5177\u901a\u5e38\u90fd\u4f1a\u8981\u6c42\u5728\u5165\u53e3\u7ec4\u4ef6\u6ce8\u5165<code>context<\/code>\uff0c\u800c\u5728<code>Taro<\/code>\u4e2d\u5165\u53e3\u6587\u4ef6\u662f\u4e0d\u80fd\u6e32\u67d3 UI \u7684\uff0c\u53ea\u8981\u6ce8\u610f\u8fd9\u70b9\u5373\u53ef\u3002<\/p>\n<p>\u5728 Vue \u751f\u6001\u5708\u6211\u4eec\u63a8\u8350\u4f7f\u7528<code>Vuex<\/code>\u3002React \u751f\u6001\u5708\u72b6\u6001\u7ba1\u7406\u5de5\u5177\u767e\u82b1\u9f50\u653e\uff0c\u8003\u8651\u5230\u4f7f\u7528<code>Taro<\/code>\u7684\u5f00\u53d1\u8005\u5f88\u591a\u5e94\u7528\u4f1a\u7f16\u8bd1\u5230\u5c0f\u7a0b\u5e8f\uff0c\u6211\u4eec\u63a8\u8350\u51e0\u4e2a\u5728\u6027\u80fd\u6216\u4f53\u79ef\u4e0a\u6709\u4f18\u52bf\u7684\u72b6\u6001\u7ba1\u7406\u5de5\u5177\uff1a<\/p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/cn.mobx.js.org\/\" title=\"mobx-react\">mobx-react<\/a>: \u548c Vuex \u4e00\u6837\u54cd\u5e94\u5f0f\u7684\u72b6\u6001\u7ba1\u7406\u5de5\u5177<\/li>\n<li><a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/jamiebuilds\/unstated-next\" title=\"unstaged\">unstaged<\/a>: \u57fa\u4e8e React Hooks \u7684\u6781\u7b80\u72b6\u6001\u7ba1\u7406\u5de5\u5177\uff0c\u538b\u7f29\u4f53\u79ef\u53ea\u6709 200 \u5b57\u8282<\/li>\n<li><a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/recoiljs.org\/\" title=\"Recoil\">Recoil<\/a>: Facebook \u63a8\u51fa\u7684\u57fa\u4e8e React Hooks \u7684\u72b6\u6001\u7ba1\u7406\u5de5\u5177<\/li>\n<\/ul>\n<h3>CSS \u5de5\u5177<\/h3>\n<p>\u5728<code>Taro<\/code>\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u81ea\u7531\u5730\u4f7f\u7528 CSS \u9884\u5904\u7406\u5668\u548c\u540e\u5904\u7406\u5668\uff0c\u4f7f\u7528\u7684\u65b9\u6cd5\u4e5f\u975e\u5e38\u7b80\u5355\uff0c\u53ea\u8981\u5728\u7f16\u8bd1\u914d\u7f6e\u6dfb\u52a0\u76f8\u5173\u7684\u63d2\u4ef6\u5373\u53ef\uff1a<\/p>\n<p>config\/index.js<\/p>\n<pre><code class=\"language-javascript\">const config = {\n  projectName: &#039;v2ex&#039;,\n  date: &#039;2018-8-3&#039;,\n  designWidth: 750,\n  sourceRoot: &#039;src&#039;,\n  outputRoot: &#039;dist&#039;,\n  plugins: [\n    &#039;@tarojs\/plugin-sass&#039;, \/\/ \u4f7f\u7528 Sass\n    \/\/ &#039;@tarojs\/plugin-less&#039;, \/\/ \u4f7f\u7528 Less\n    \/\/ &#039;@tarojs\/plugin-stylus&#039;, \/\/ \u4f7f\u7528 Stylus\n  ],\n  defineConstants: {\n  },\n  mini: {\n\n  },\n  h5: {\n    publicPath: &#039;\/&#039;,\n    staticDirectory: &#039;static&#039;,\n    module: {\n      postcss: {\n        autoprefixer: {\n          enable: true\n        }\n      }\n    }\n  }\n}\n\nmodule.exports = function (merge) {\n  if (process.env.NODE_ENV === &#039;development&#039;) {\n    return merge({}, config, require(&#039;.\/dev&#039;))\n  }\n  return merge({}, config, require(&#039;.\/prod&#039;))\n}<\/code><\/pre>\n<blockquote>\n<p>\u9664\u4e86 CSS \u9884\u5904\u7406\u5668\u4e4b\u5916\uff0cTaro \u8fd8\u652f\u6301 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/css-modules\" title=\"CSS Modules\">CSS Modules<\/a> \u548c <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/css-in-js\" title=\"CSS-in-JS\">CSS-in-JS<\/a>\u3002 \u539f\u7406\u4e0a\u8fd8\u652f\u6301\u66f4\u591a CSS \u5de5\u5177\uff0c\u6211\u4eec\u5c06\u5728<strong>\u81ea\u5b9a\u4e49\u7f16\u8bd1<\/strong>\u7ee7\u7eed\u8ba8\u8bba\u8fd9\u4e2a\u95ee\u9898\u3002<\/p>\n<\/blockquote>\n<h3>\u6e32\u67d3 HTML<\/h3>\n<p>\u5728\u5e16\u5b50\u8be6\u60c5\u7ec4\u4ef6\uff08<code>ThreadDetail<\/code>\uff09\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u4e86\u5185\u7f6e\u7ec4\u4ef6<code>RichText<\/code>\u6765\u6e32\u67d3 HTML\uff0c\u4f46\u8fd9\u4e2a\u7ec4\u4ef6\u7684\u517c\u5bb9\u6027\u4e0d\u597d\uff0c\u65e0\u6cd5\u5728\u6240\u6709\u7aef\u90fd\u6b63\u5e38\u4f7f\u7528\uff0c\u67d0\u4e9b\u7279\u5b9a\u7684 HTML \u5143\u7d20\u4e5f\u65e0\u6cd5\u6e32\u67d3\u3002<\/p>\n<p>\u5e78\u8fd0\u7684\u662f\uff0cTaro \u5185\u7f6e\u4e86 HTML \u6e32\u67d3\uff0c\u4f7f\u7528\u65b9\u6cd5\u4e5f\u548c React\/Vue \u5728 Web \u5f00\u53d1\u4e2d\u6ca1\u4ec0\u4e48\u533a\u522b\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.jsx<\/code><\/p>\n<pre><code class=\"language-html\">- &lt;RichText nodes={reply.content} className=&#039;content&#039; \/&gt;\n+ &lt;View dangerouslySetInnerHTML={{ __html: reply.content }} className=&#039;content&#039;&gt;&lt;\/View&gt;<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.vue<\/code><\/p>\n<pre><code class=\"language-html\">- &lt;rich-text :nodes=&quot;reply.content_rendered | html&quot; class=&#039;content&#039; \/&gt;\n+ &lt;view v-html=&quot;reply.content_rendered | html&quot; class=&#039;content&#039; \/&gt;<\/code><\/pre>\n<blockquote>\n<p>Taro \u5185\u7f6e\u7684 HTML \u6e32\u67d3\u529f\u80fd\u4e0d\u4ec5\u53ef\u4ee5\u6309 Web \u5f00\u53d1\u7684\u65b9\u5f0f\u53bb\u4f7f\u7528\uff0c\u4e5f\u652f\u6301\u81ea\u5b9a\u4e49\u6837\u5f0f\u3001\u81ea\u5b9a\u4e49\u6e32\u67d3\u3001\u81ea\u5b9a\u4e49\u4e8b\u4ef6\u8fd9\u6837\u7684\u9ad8\u7ea7\u529f\u80fd\u3002 \u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/html\" title=\"HTML \u6e32\u67d3\u6587\u6863\">HTML \u6e32\u67d3\u6587\u6863<\/a> \u4e86\u89e3\u66f4\u591a\u3002<\/p>\n<\/blockquote>\n<h3>\u6027\u80fd\u4f18\u5316<\/h3>\n<h4>\u865a\u62df\u5217\u8868<\/h4>\n<p>\u5728\u5e16\u5b50\u5217\u8868\u7ec4\u4ef6\uff08<code>ThreadList<\/code>\uff09\u4e2d\uff0c\u6211\u4eec\u76f4\u63a5\u6e32\u67d3\u4ece\u8fdc\u7a0b\u5f97\u6765\u7684\u6570\u636e\u3002\u8fd9\u6837\u505a\u6ca1\u6709\u4ec0\u4e48\u95ee\u9898\uff0c\u4f46\u5982\u679c\u6211\u4eec\u7684\u6570\u636e\u975e\u5e38\u5e9e\u5927\uff0c\u6216\u8005\u5217\u8868\u6e32\u67d3\u7684 DOM \u7ed3\u6784\u5f02\u5e38\u590d\u6742\uff0c\u8fd9\u5c31\u53ef\u80fd\u4f1a\u4ea7\u751f\u6027\u80fd\u95ee\u9898\u3002<\/p>\n<p>\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e00\u95ee\u9898\uff0c<code>Taro<\/code>\u5185\u7f6e\u4e86\u865a\u62df\u5217\u8868\uff08<code>VirtualList<\/code>\uff09\u529f\u80fd\uff0c\u6bd4\u8d77\u5168\u91cf\u6e32\u67d3\u6240\u6709\u5217\u8868\u6570\u636e\uff0c\u6211\u4eec\u53ea\u9700\u8981\u6e32\u67d3\u5f53\u524d\u53ef\u89c6\u533a\u57df(<code>visable viewport<\/code>)\u7684\u89c6\u56fe\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_list.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">import React from &#039;react&#039;\nimport { View, Text } from &#039;@tarojs\/components&#039;\nimport { Thread } from &#039;.\/thread&#039;\nimport { Loading } from &#039;.\/loading&#039;\nimport VirtualList from `@tarojs\/components\/virtual-list`\n\nimport &#039;.\/thread.css&#039;\n\nconst Row = React.memo(({ thread }) =&gt; {\n  return (\n    &lt;Thread\n      key={thread.id}\n      node={thread.node}\n      title={thread.title}\n      last_modified={thread.last_modified}\n      replies={thread.replies}\n      tid={thread.id}\n      member={thread.member}\n    \/&gt;\n  )\n})\n\nclass ThreadList extends React.Component {\n  static defaultProps = {\n    threads: [],\n    loading: true\n  }\n\n  render () {\n    const { loading, threads } = this.props\n\n    if (loading) {\n      return &lt;Loading \/&gt;\n    }\n\n    const element = (\n      &lt;VirtualList\n        height={800} \/* \u5217\u8868\u7684\u9ad8\u5ea6 *\/\n        width=&#039;100%&#039; \/* \u5217\u8868\u7684\u5bbd\u5ea6 *\/\n        itemData={threads} \/* \u6e32\u67d3\u5217\u8868\u7684\u6570\u636e *\/\n        itemCount={threads.length} \/*  \u6e32\u67d3\u5217\u8868\u7684\u957f\u5ea6 *\/\n        itemSize={100} \/* \u5217\u8868\u5355\u9879\u7684\u9ad8\u5ea6  *\/\n      &gt;\n        {Row} \/* \u5217\u8868\u5355\u9879\u7ec4\u4ef6\uff0c\u8fd9\u91cc\u53ea\u80fd\u4f20\u5165\u4e00\u4e2a\u7ec4\u4ef6 *\/\n      &lt;\/VirtualList&gt;\n    )\n\n    return (\n      &lt;View className=&#039;thread-list&#039;&gt;\n        {element}\n      &lt;\/View&gt;\n    )\n  }\n}\n\nexport { ThreadList }<\/code><\/pre>\n<p>Vue\u7248\u672c<\/p>\n<ul>\n<li><code>app.js<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-javascript\">\/\/ \u5728\u5165\u53e3\u6587\u4ef6\u65b0\u589e\u4f7f\u7528\u63d2\u4ef6\nimport VirtualList from `@tarojs\/components\/virtual-list`\nVue.use(VirtualList)<\/code><\/pre>\n<ul>\n<li><code>src\/components\/row.vue<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;thread\n    :key=&quot;thread.id&quot;\n    :node=&quot;thread.node&quot;\n    :title=&quot;thread.title&quot;\n    :last_modified=&quot;thread.last_modified&quot;\n    :replies=&quot;thread.replies&quot;\n    :tid=&quot;thread.id&quot;\n    :member=&quot;thread.member&quot;\n  \/&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport Thread from &#039;.\/thread.vue&#039;\nexport default {\n  components: {\n    &#039;thread&#039;: Thread\n  },\n  props: [&#039;index&#039;, &#039;data&#039;, &#039;css&#039;],\n  computed:{\n    thread(){\n      return this.data[this.index]\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<ul>\n<li><code>src\/components\/thread_list.vue<\/code><\/li>\n<\/ul>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;view className=&#039;thread-list&#039;&gt;\n    &lt;loading v-if=&quot;loading&quot; \/&gt;\n    &lt;virtual-list\n      v-else\n      :height=&quot;500&quot;\n      :item-data=&quot;threads&quot;\n      :item-count=&quot;threads.length&quot;\n      :item-size=&quot;100&quot;\n      :item=&quot;Row&quot;\n      width=&quot;100%&quot;\n    \/&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script &gt;\nimport Vue from &#039;vue&#039;\nimport Loading from &#039;.\/loading.vue&#039;\nimport Thread from &#039;.\/thread.vue&#039;\nimport Row from &#039;.\/row.vue&#039;\nexport default {\n  components: {\n    &#039;loading&#039;: Loading,\n    &#039;thread&#039;: Thread\n  },\n  props: {\n    threads: {\n      type: Array,\n      default: []\n    },\n    loading: {\n      type: Boolean,\n      default: true\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<blockquote>\n<p>\u5728\u6587\u6863 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/virtual-list\" title=\"\u865a\u62df\u5217\u8868\">\u865a\u62df\u5217\u8868<\/a> \u53ef\u4ee5\u627e\u5230\u865a\u62df\u5217\u8868\u7684\u4e00\u4e9b\u9ad8\u7ea7\u7528\u6cd5\uff0c\u4f8b\u5982\uff1a\u65e0\u9650\u6eda\u52a8\u3001\u6eda\u52a8\u504f\u79fb\u3001\u6eda\u52a8\u4e8b\u4ef6\u7b49\u3002<\/p>\n<\/blockquote>\n<h4>\u9884\u6e32\u67d3<\/h4>\n<p>\u73b0\u5728\u6211\u4eec\u6765\u5b9e\u73b0\u6700\u540e\u4e00\u4e2a\u9875\u9762\uff1a\u8282\u70b9\u5217\u8868\u9875\u9762\u3002\u8fd9\u4e2a\u9875\u9762\u672c\u8d28\u8bf4\u5c31\u662f\u6e32\u67d3\u4e00\u4e2a\u5b58\u5728\u672c\u5730\u7684\u5de8\u5927\u5217\u8868\uff1a<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/nodes\/nodes.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">import React from &#039;react&#039;\nimport { View, Text, Navigator } from &#039;@tarojs\/components&#039;\nimport allNodes from &#039;.\/all_node&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\n\nimport &#039;.\/nodes.css&#039;\n\nfunction Nodes () {\n  const element = allNodes.map(item =&gt; {\n    return (\n      &lt;View key={item.title} className=&#039;container&#039;&gt;\n        &lt;View className=&#039;title&#039;&gt;\n          &lt;Text style=&#039;margin-left: 5px&#039;&gt;{item.title}&lt;\/Text&gt;\n        &lt;\/View&gt;\n        &lt;View className=&#039;nodes&#039;&gt;\n          {item.nodes.map(node =&gt; {\n            return (\n              &lt;Navigator\n                className=&#039;tag&#039;\n                url={`\/pages\/node_detail\/node_detail${api.queryString(node)}`}\n                key={node.full_name}\n              &gt;\n                &lt;Text&gt;{node.full_name}&lt;\/Text&gt;\n              &lt;\/Navigator&gt;\n            )\n          })}\n        &lt;\/View&gt;\n      &lt;\/View&gt;\n    )\n  })\n  return &lt;View className=&#039;node-container&#039;&gt;{element}&lt;\/View&gt;\n}\n\nexport default Nodes<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/nodes\/nodes.vue<\/code><\/p>\n<pre><code class=\"language-javascript\">&lt;template&gt;\n  &lt;view class=&#039;node-container&#039;&gt;\n    &lt;view v-for=&quot;item in allNodes&quot; :key=&quot;item.title&quot; class=&#039;container&#039;&gt;\n      &lt;view class=&#039;title&#039;&gt;\n        &lt;text style=&#039;margin-left: 5px&#039;&gt;{{item.title}}&lt;\/text&gt;\n      &lt;\/view&gt;\n      &lt;view class=&#039;nodes&#039;&gt;\n        &lt;navigator\n          v-for=&quot;node in item.nodes&quot;\n          :key=&quot;node.full_name&quot;\n          class=&#039;tag&#039;\n          :url=&quot;node | url&quot;\n        &gt;\n          &lt;text&gt;{{node.full_name}}&lt;\/text&gt;\n        &lt;\/navigator&gt;\n      &lt;\/view&gt;\n    &lt;\/view&gt;\n  &lt;\/view&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport Vue from &#039;vue&#039;\nimport allNodes from &#039;.\/all_node&#039;\nimport api from &#039;..\/..\/utils\/api&#039;\nimport &#039;.\/nodes.css&#039;\n\nfunction getURL (node) {\n  return `\/pages\/node_detail\/node_detail${api.queryString(node)}`\n}\n\nexport default {\n  data () {\n    return {\n      allNodes\n    }\n  },\n  filters: {\n    url (node) {\n      return getURL(node)\n    }\n  }\n}\n&lt;\/script&gt;<\/code><\/pre>\n<p>\u8fd9\u4e2a\u65f6\u5019\u6211\u4eec\u6574\u4e2a\u5e94\u7528\u5c31\u5b8c\u6210\u4e86\u3002\u4f46\u5982\u679c\u628a\u8fd9\u4e2a\u5e94\u7528\u653e\u5728\u771f\u673a\u5c0f\u7a0b\u5e8f\u4e2d\uff0c\u5c24\u5176\u662f\u4e00\u4e9b\u6027\u80fd\u4e0d\u9ad8\u7684\u771f\u673a\u4e2d\uff0c\u5207\u6362\u5230\u6b64\u9875\u9762\u7684\u65f6\u95f4\u53ef\u80fd\u4f1a\u6bd4\u8f83\u957f\uff0c\u4f1a\u6709\u4e00\u6bb5\u767d\u5c4f\u65f6\u95f4\u3002<\/p>\n<p>\u8fd9\u662f\u7531\u4e8e<code>Taro<\/code>\u7684\u6e32\u67d3\u673a\u5236\u5bfc\u81f4\u7684\uff1a\u5728\u9875\u9762\u521d\u59cb\u5316\u65f6\uff0c\u539f\u751f\u5c0f\u7a0b\u5e8f\u53ef\u4ee5\u4ece\u672c\u5730\u76f4\u63a5\u53d6\u6570\u636e\u6e32\u67d3\uff0c\u4f46<code>Taro<\/code>\u4f1a\u628a\u521d\u59cb\u6570\u636e\u901a\u8fc7 React\/Vue \u6e32\u67d3\u6210\u4e00\u9897 DOM \u6811\uff0c\u7136\u540e\u5c06\u8fd9\u9897 DOM \u6811\u5e8f\u5217\u5316\u4e4b\u540e\u4ea4\u7ed9\u5c0f\u7a0b\u5e8f\u6e32\u67d3\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u6bd4\u8d77\u539f\u751f\u5c0f\u7a0b\u5e8f<code>Taro<\/code>\u4f1a\u5728\u9875\u9762\u521d\u59cb\u5316\u65f6\u591a\u4e00\u6b21\u8c03\u7528<code>setData<\/code>\u51fd\u6570\u7684\u652f\u51fa\u2014\u2014\u800c\u5927\u90e8\u5206\u5c0f\u7a0b\u5e8f\u7684\u6027\u80fd\u95ee\u9898\u662f<code>setData<\/code>\u6570\u636e\u8fc7\u5927\u5bfc\u81f4\u7684\u3002<\/p>\n<p>\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff0c<code>Taro<\/code>\u5f15\u5165\u4e86\u4e00\u79cd\u540d\u4e3a\u9884\u6e32\u67d3\uff08<code>Prerender<\/code>\uff09\u7684\u6280\u672f\uff0c\u548c\u670d\u52a1\u7aef\u6e32\u67d3\u4e00\u6837\uff0c\u5728 Taro CLI \u76f4\u63a5\u5c06\u8981\u6e32\u67d3\u7684\u9875\u9762\u8f6c\u6362\u4e3a<code>wxml<\/code>\u5b57\u7b26\u4e32\uff0c\u8fd9\u6837\u5c31\u83b7\u5f97\u4e86\u4e0e\u539f\u751f\u5c0f\u7a0b\u5e8f\u4e00\u81f4\u751a\u81f3\u66f4\u5feb\u7684\u901f\u5ea6\u3002<\/p>\n<p>\u4f7f\u7528\u9884\u6e32\u67d3\u4e5f\u975e\u5e38\u7b80\u5355\uff0c\u6211\u4eec\u53ea\u8981\u8fdb\u884c\u7b80\u5355\u7684\u914d\u7f6e\u5373\u53ef\uff1a<\/p>\n<p>config\/prod.js<\/p>\n<pre><code class=\"language-javascript\">const config = {\n  ...\n  mini: {\n    prerender: {\n      include: [&#039;pages\/nodes\/nodes&#039;], \/\/ `pages\/nodes\/nodes` \u4e5f\u4f1a\u53c2\u4e0e prerender\n    }\n  }\n};\n\n\/\/ \u6211\u4eec\u8fd9\u91cc\u5728\u7f16\u8bd1\u751f\u4ea7\u6a21\u5f0f\u65f6\u624d\u5f00\u542f\u9884\u6e32\u67d3\n\/\/ \u5982\u679c\u9700\u8981\u5f00\u53d1\u65f6\u4e5f\u5f00\u542f\uff0c\u90a3\u5c31\u628a\u914d\u7f6e\u653e\u5728 `config\/index` \u6216 `config\/dev`\nmodule.exports = config<\/code><\/pre>\n<blockquote>\n<p>\u9884\u6e32\u67d3\u7684\u914d\u7f6e\u652f\u6301\u6761\u4ef6\u6e32\u67d3\u9875\u9762\u3001\u6761\u4ef6\u6e32\u67d3\u903b\u8f91\u3001\u81ea\u5b9a\u4e49\u6e32\u67d3\u51fd\u6570\u7b49\u529f\u80fd\uff0c\u8be6\u60c5\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/prerender\" title=\"\u9884\u6e32\u67d3\u6587\u6863\">\u9884\u6e32\u67d3\u6587\u6863<\/a>\u3002<\/p>\n<\/blockquote>\n<h3>\u6253\u5305\u4f53\u79ef<\/h3>\n<p>\u9ed8\u8ba4\u800c\u8a00\u4f7f\u7528\u751f\u4ea7\u6a21\u5f0f\u6253\u5305\uff0c<code>Taro<\/code>\u5c31\u4f1a\u4f18\u5316\u6253\u5305\u4f53\u79ef\u3002\u4f46\u503c\u5f97\u6ce8\u610f\uff0c<code>Taro<\/code>\u9ed8\u8ba4\u7684\u6253\u5305\u914d\u7f6e\u662f\u4e3a\u4e86\u8ba9\u591a\u6570\u9879\u76ee\u548c\u9700\u6c42\u90fd\u53ef\u4ee5\u8fd0\u884c\uff0c\u800c\u4e0d\u662f\u9488\u5bf9\u4efb\u4f55\u9879\u76ee\u7684\u6700\u4f18\u9009\u62e9\u3002\u56e0\u6b64\u53ef\u4ee5\u5728<code>Taro<\/code>\u914d\u7f6e\u7684\u57fa\u7840\u4e4b\u4e0a\u518d\u9488\u5bf9\u81ea\u5df1\u7684\u9879\u76ee\u8fdb\u884c\u4f18\u5316\u3002<\/p>\n<h4>JavaScript<\/h4>\n<p>\u5728<code>Taro<\/code>\u5e94\u7528\u4e2d\uff0c\u6240\u6709 Java(Type)Script \u90fd\u662f\u901a\u8fc7<code>babel.config.js<\/code>\u914d\u7f6e\u7684\uff0c\u5177\u4f53\u6765\u8bf4\u662f\u4f7f\u7528 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.npmjs.com\/package\/babel-preset-taro\" title=\"babel-prest-taro\">babel-prest-taro<\/a> \u8fd9\u4e2a Babel \u63d2\u4ef6\u7f16\u8bd1\u7684\u3002<\/p>\n<p>\u9ed8\u8ba4\u800c\u8a00<code>Taro<\/code>\u4f1a\u517c\u5bb9\u6240\u6709 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.npmjs.com\/package\/@babel\/preset-env\" title=\"@babel\/preset-env\">@babel\/preset-env<\/a> \u652f\u6301\u7684\u8bed\u6cd5\uff0c\u5e76\u517c\u5bb9\u5230 iOS 9 \u548c Android 5\uff0c\u5982\u679c\u4e0d\u9700\u8981\u90a3\u4e48\u9ad8\u7684\u517c\u5bb9\u6027\uff0c\u6216\u8005\u4e0d\u9700\u8981\u67d0\u4e9b ES2015+ \u8bed\u6cd5\u652f\u6301\uff0c\u53ef\u4ee5\u81ea\u884c\u914d\u7f6e<code>babel.config.js<\/code>\u8fbe\u5230\u7f29\u5c0f\u6253\u5305\u4f53\u79ef\u6548\u679c\u3002<\/p>\n<p>\u4f8b\u5982\u6211\u4eec\u53ef\u4ee5\u628a\u517c\u5bb9\u6027\u63d0\u5347\u5230 iOS 12\uff1a<\/p>\n<p>babel.config.js<\/p>\n<pre><code class=\"language-javascript\">\/\/ babel.config.js\nmodule.exports = {\n  presets: [\n    [&#039;taro&#039;, {\n      targets: {\n        ios: &#039;12&#039;\n      }\n    }]\n  ]\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/babeljs.io\/\" title=\"Babel \u6587\u6863\">Babel \u6587\u6863<\/a> \u4e86\u89e3\u66f4\u591a\u81ea\u5b9a\u4e49\u914d\u7f6e\u7684\u4fe1\u606f\u3002<\/p>\n<h4>\u6253\u5305\u4f53\u79ef\u5206\u6790<\/h4>\n<p><code>Taro<\/code>\u4f7f\u7528 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.webpackjs.com\/\" title=\"Webpack\">Webpack<\/a> \u4f5c\u4e3a\u5185\u90e8\u7684\u6253\u5305\u7cfb\u7edf\uff0c\u6709\u65f6\u5019\u5f53\u6211\u4eec\u7684\u4e1a\u52a1\u4ee3\u7801\u4f7f\u7528\u4e86<code>require<\/code>\u8bed\u6cd5\u6216\u8005<code>import default<\/code>\u8bed\u6cd5\uff0cWebpack \u5e76\u4e0d\u80fd\u7ed9\u6211\u4eec\u63d0\u4f9b<code>tree-shaking<\/code>\u7684\u6548\u679c\u3002\u5728\u8fd9\u6837\u7684\u60c5\u51b5\u4e0b\u6211\u4eec\u901a\u8fc7 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.npmjs.com\/package\/webpack-bundle-analyzer\" title=\"webpack-bundle-analyzer\">webpack-bundle-analyzer<\/a> \u6765\u5206\u6790\u6211\u4eec\u4f9d\u8d56\u6253\u5305\u4f53\u79ef\uff0c\u8fd9\u4e2a\u63d2\u4ef6\u4f1a\u5728\u6d4f\u89c8\u5668\u6253\u5f00\u4e00\u4e2a\u53ef\u89c6\u5316\u7684\u56fe\u8868\u9875\u9762\u544a\u8bc9\u6211\u4eec\u5f15\u7528\u5404\u4e2a\u5305\u7684\u4f53\u79ef\u3002<\/p>\n<p>\u9996\u5148\u5b89\u88c5<code>webpack-bundle-analyzer<\/code>\u4f9d\u8d56:<\/p>\n<pre><code class=\"language-bash\">npm install webpack-bundle-analyzer -D<\/code><\/pre>\n<p>\u7136\u540e\u5728 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/config-detail\/#miniwebpackchain\" title=\"mini.webpackChain\">mini.webpackChain<\/a> \u4e2d\u6dfb\u52a0\u5982\u4e0b\u914d\u7f6e\uff1a<\/p>\n<p>config\/index<\/p>\n<pre><code class=\"language-javascript\">const config = {\n  ...\n  mini: {\n    webpackChain (chain, webpack) {\n      chain.plugin(&#039;analyzer&#039;)\n        .use(require(&#039;webpack-bundle-analyzer&#039;).BundleAnalyzerPlugin, [])\n    }\n  }\n}<\/code><\/pre>\n<p>\u8fd0\u884c\u7f16\u8bd1\u547d\u4ee4\u5b8c\u6210\u4e4b\u540e\u5c31\u53ef\u4ee5\u770b\u5230\u5404\u6587\u4ef6\u4f9d\u8d56\u5173\u7cfb\u53ca\u4f53\u79ef\u3002<\/p>\n<p>\u4e5f\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.npmjs.com\/package\/webpack-bundle-analyzer\" title=\"webpack-bundle-analyzer\">webpack-bundle-analyzer<\/a> \u6587\u6863\u4e86\u89e3\u8be6\u7ec6\u7684\u7528\u6cd5\u3002<\/p>\n<h4>\u5206\u5305<\/h4>\n<p>\u5728\u4e00\u4e9b\u60c5\u51b5\uff0c\u6211\u4eec\u5e0c\u671b\u6211\u4eec\u7684\u9875\u9762\u53ea\u6709\u5f53\u7528\u5230\u65f6\u624d\u6309\u9700\u8fdb\u884c\u52a0\u8f7d\u3002\u8fd9\u79cd\u60c5\u51b5\u5728<code>Taro<\/code>\u5e94\u7528\u88ab\u79f0\u4e3a\u5206\u5305\uff0c\u5206\u5305\u7684\u4f7f\u7528\u4e5f\u975e\u5e38\u7b80\u5355\uff0c\u53ea\u9700\u8981\u901a\u8fc7\u914d\u7f6e\u5165\u53e3\u6587\u4ef6<code>app.config.js<\/code>\u5373\u53ef\u3002<\/p>\n<p>\u5047\u8bbe\u6211\u4eec\u9700\u8981\u628a\u521a\u521a\u5b9e\u73b0\u9884\u6e32\u67d3\u7684\u6240\u6709\u8282\u70b9\u9875\u9762\u8fdb\u884c\u5206\u5305\uff1a<\/p>\n<p>src\/app.config.js<\/p>\n<pre><code class=\"language-javascript\">export default {\n  pages: [\n    &#039;pages\/index\/index&#039;,\n    \/\/ &#039;pages\/nodes\/nodes&#039;, \u628a\u8981\u5206\u5305\u7684\u9875\u9762\u4ece `pages` \u5b57\u6bb5\u4e2d\u5220\u9664\n    &#039;pages\/hot\/hot&#039;,\n    &#039;pages\/node_detail\/node_detail&#039;,\n    &#039;pages\/thread_detail\/thread_detail&#039;\n  ],\n  \/\/ \u5728 `subpackages` \u5b57\u6bb5\u6dfb\u52a0\u5206\u5305\n  &quot;subpackages&quot;: [\n    {\n      &quot;root&quot;: &quot;pages&quot;,\n      &quot;pages&quot;: [\n        &quot;nodes\/nodes&quot;\n      ]\n    }\n  ]\n  tabBar: {\n    list: [{\n      &#039;iconPath&#039;: &#039;resource\/latest.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/lastest_on.png&#039;,\n      pagePath: &#039;pages\/index\/index&#039;,\n      text: &#039;\u6700\u65b0&#039;\n    }, {\n      &#039;iconPath&#039;: &#039;resource\/hotest.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/hotest_on.png&#039;,\n      pagePath: &#039;pages\/hot\/hot&#039;,\n      text: &#039;\u70ed\u95e8&#039;\n    }, {\n      &#039;iconPath&#039;: &#039;resource\/node.png&#039;,\n      &#039;selectedIconPath&#039;: &#039;resource\/node_on.png&#039;,\n      pagePath: &#039;pages\/nodes\/nodes&#039;,\n      text: &#039;\u8282\u70b9&#039;\n    }],\n    &#039;color&#039;: &#039;#000&#039;,\n    &#039;selectedColor&#039;: &#039;#56abe4&#039;,\n    &#039;backgroundColor&#039;: &#039;#fff&#039;,\n    &#039;borderStyle&#039;: &#039;white&#039;\n  },\n  window: {\n    backgroundTextStyle: &#039;light&#039;,\n    navigationBarBackgroundColor: &#039;#fff&#039;,\n    navigationBarTitleText: &#039;V2EX&#039;,\n    navigationBarTextStyle: &#039;black&#039;\n  }\n}<\/code><\/pre>\n<h3>\u81ea\u5b9a\u4e49\u7f16\u8bd1<\/h3>\n<p>\u5728\u7279\u5b9a\u7684\u60c5\u51b5\u4e0b\uff0c<code>Taro<\/code>\u81ea\u5e26\u7684\u7f16\u8bd1\u7cfb\u7edf\u6ca1\u6709\u529e\u6cd5\u6ee1\u8db3\u6211\u4eec\u7684\u7f16\u8bd1\u9700\u6c42\uff0c\u8fd9\u65f6<code>Taro<\/code>\u63d0\u4f9b\u4e86\u4e24\u79cd\u62d3\u5c55\u7f16\u8bd1\u7684\u65b9\u6848\uff1a<\/p>\n<h4>\u4f7f\u7528 Webpack \u8fdb\u884c\u62d3\u5c55<\/h4>\n<p>\u5728<strong>\u6253\u5305\u4f53\u79ef\u5206\u6790<\/strong>\u4e2d\u6211\u4eec\u5728<code>mini.webpackChain<\/code>\u6dfb\u52a0\u4e86\u4e00\u4e2a Webpack \u63d2\u4ef6\uff0c\u8fbe\u5230\u4e86\u6253\u5305\u4f53\u79ef\/\u4f9d\u8d56\u5206\u6790\u7684\u6548\u679c\u3002<\/p>\n<p>\u4e8b\u5b9e\u4e0a\u901a\u8fc7<code>mini.webpackChain<\/code>\u8fd9\u4e2a\u914d\u7f6e\u6211\u4eec\u53ef\u4ee5\u51e0\u4e4e\u4f7f\u7528\u4efb\u4f55 Webpack \u751f\u6001\u7684\u63d2\u4ef6\u548c loader\uff0c\u4f8b\u5982\u6211\u4eec\u60f3\u4f7f\u7528<code>CoffeeScript<\/code>\u6765\u8fdb\u884c\u5f00\u53d1\uff1a<\/p>\n<p>config\/index<\/p>\n<pre><code class=\"language-javascript\">const config = {\n  ...\n  mini: {\n    webpackChain (chain, webpack) {\n      chain.merge({\n          module: {\n              rule: {\n                  test: \/\\.coffee$\/,\n                  use: [ &#039;coffee-loader&#039; ]\n              }\n          }\n      })\n    }\n  }\n}<\/code><\/pre>\n<p>\u540c\u6837\uff0c\u4e4b\u524d\u6211\u4eec\u63d0\u5230\u8fc7\u7684<code>CSS Modules<\/code>\u4e5f\u53ef\u4ee5\u901a\u8fc7 Webpack \u7684\u5f62\u5f0f\u8fdb\u884c\u62d3\u5c55\u652f\u6301\u3002\u8be6\u60c5\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/neutrinojs\/webpack-chain\" title=\"webpack-chain\">webpack-chain<\/a> \u6587\u6863\u4e86\u89e3\u8be6\u7ec6\u7684\u7528\u6cd5\u3002<\/p>\n<h4>\u4f7f\u7528\u63d2\u4ef6\u5316\u7cfb\u7edf\u8fdb\u884c\u62d3\u5c55<\/h4>\n<p>\u5728<strong>CSS \u5de5\u5177<\/strong>\u6211\u4eec\u5df2\u7ecf\u4f7f\u7528\u4e86\u540d\u4e3a<code>@tarojs\/plugin-sass<\/code>\u7684\u63d2\u4ef6\u6765\u5b9e\u73b0\u5bf9<code>Sass<\/code>\u7684\u652f\u6301\u3002\u6bd4\u8d77\u4f7f\u7528 Webpack \u62d3\u5c55\u7f16\u8bd1\uff0c<code>Taro<\/code>\u7684\u63d2\u4ef6\u529f\u80fd\u4e0d\u7528\u5728\u6bcf\u4e2a\u7aef\u90fd\u5bf9 Webpack \u8fdb\u884c\u914d\u7f6e\uff0c\u53ea\u8981\u4f7f\u7528\u63d2\u4ef6\u5373\u53ef\u3002<\/p>\n<p>\u9664\u6b64\u4e4b\u5916\uff0c<code>Taro<\/code>\u7684\u63d2\u4ef6\u5316\u529f\u80fd\u8fd8\u53ef\u4ee5\u62d3\u5c55<code>Taro CLI<\/code>\u7f16\u8bd1\u547d\u4ee4\uff0c\u62d3\u5c55\u7f16\u8bd1\u6d41\u7a0b\u548c\u7f16\u8bd1\u5e73\u53f0\uff0c\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/plugin\" title=\"\u63d2\u4ef6\u529f\u80fd\u6587\u6863\">\u63d2\u4ef6\u529f\u80fd\u6587\u6863<\/a> \u4e86\u89e3\u66f4\u591a\u81ea\u5b9a\u4e49\u914d\u7f6e\u7684\u4fe1\u606f\u3002<\/p>\n<blockquote>\n<p>\u9664\u4e86\u4ee5\u4e0a\u4e24\u79cd\u65b9\u5f0f\u5916\uff0cTaro \u8fd8\u63d0\u4f9b\u5927\u91cf\u7684\u7f16\u8bd1\u76f8\u5173\u9009\u9879\uff0c\u53ef\u4ee5\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/config-detail\" title=\"\u7f16\u8bd1\u914d\u7f6e\u8be6\u60c5\">\u7f16\u8bd1\u914d\u7f6e\u8be6\u60c5<\/a> \u6587\u6863\u4e86\u89e3\u66f4\u591a\u3002<\/p>\n<\/blockquote>\n<h2>\u591a\u7aef\u5f00\u53d1<\/h2>\n<h3>\u8de8\u5e73\u53f0\u5f00\u53d1<\/h3>\n<p>\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u4e0d\u540c\u5e73\u53f0\u7684\u8868\u73b0\u6216\u4e1a\u52a1\u903b\u8f91\u6709\u8d28\u7684\u4e0d\u540c\u3002\u5728\u8fd9\u6837\u7684\u60c5\u51b5\u4e0b\u6211\u4eec\u662f\u6ca1\u6709\u529e\u6cd5\u505a\u5230\u300c\u4e00\u5957\u4ee3\u7801\u8d70\u5929\u4e0b\u300d\u7684\u3002<\/p>\n<p>\u4f8b\u5982\u6211\u4eec\u6b63\u5728\u5b9e\u73b0 V2EX \u8bba\u575b\u5e94\u7528\uff0c\u5f53\u524d\u7684 API \u6ca1\u6709\u529e\u6cd5\u5728\u6d4f\u89c8\u5668\u4e2d\u8de8\u57df\u8c03\u7528\uff0c\u56e0\u6b64\u6211\u4eec\u9700\u8981\u5728 H5 \u7aef\u4f7f\u7528\u53e6\u4e00\u4efd API\u3002\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7<strong>\u5185\u7f6e\u73af\u5883\u53d8\u91cf<\/strong>\u6765\u89e3\u51b3\uff1a<\/p>\n<pre><code class=\"language-javascript\">- import api from &#039;..\/..\/utils\/api&#039;\n\/\/ \u6211\u4eec\u53ef\u4ee5\u6839\u636e\u4e0d\u540c\u7684\u5e73\u53f0\uff0c\u5f15\u5165\u4e0d\u540c\u7684 API\n+ let api\n+ if (process.env.TARO_ENV === &#039;weapp&#039;) {\n+  api = require(&#039;..\/..\/utils\/api-weapp&#039;)\n+ } else if (process.env.TARO_ENV === &#039;h5&#039;) {\n+  api = require(&#039;..\/..\/utils\/api-h5&#039;)\n+ }<\/code><\/pre>\n<p><code>Taro<\/code>\u8fd8\u63d0\u4f9b\u4e86<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/envs#\u7edf\u4e00\u63a5\u53e3\u7684\u591a\u7aef\u6587\u4ef6\" title=\"\u7edf\u4e00\u63a5\u53e3\u7684\u591a\u7aef\u6587\u4ef6\">\u7edf\u4e00\u63a5\u53e3\u7684\u591a\u7aef\u6587\u4ef6<\/a>\uff0c\u901a\u8fc7\u4e0d\u540c\u7684\u547d\u540d\u65b9\u5f0f\u5bfb\u627e\u4f9d\u8d56\uff0c\u5728\u8fd9\u7c7b\u60c5\u51b5\u4e0b\uff0c\u6211\u4eec\u53ef\u4ee5\u4fdd\u7559\uff1a<\/p>\n<pre><code class=\"language-javascript\">import api from &#039;..\/..\/utils\/api&#039;<\/code><\/pre>\n<p>\u8bed\u53e5\u539f\u5c01\u4e0d\u52a8\uff0c\u4fee\u6539\u6211\u4eec\u7684\u6587\u4ef6\u7ed3\u6784\uff0c\u5728\u6587\u4ef6\u540d\u548c\u540e\u7f00\u540d\u4e4b\u95f4\u52a0\u4e0a\u5e73\u53f0\u7684\u540d\u5b57\uff1a<\/p>\n<pre><code>.\n\u2514\u2500\u2500 utils\n    \u251c\u2500\u2500 api.h5.js\n    \u251c\u2500\u2500 api.weapp.js\n    \u2514\u2500\u2500 index.js<\/code><\/pre>\n<blockquote>\n<p>\u9664\u4e86\u300c\u5185\u7f6e\u73af\u5883\u53d8\u91cf\u300d\u548c\u300c\u7edf\u4e00\u63a5\u53e3\u7684\u591a\u7aef\u6587\u4ef6\u300d\u4e4b\u5916\uff0c<code>Taro<\/code>\u8fd8\u63d0\u4f9b\u4e86\u522b\u7684\u8de8\u5e73\u53f0\u5f00\u53d1\u89e3\u51b3\u65b9\u6848\uff0c\u53ef\u4ee5\u8bbf\u95ee\u6587\u6863 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/guide\/envs\" title=\"\u8de8\u5e73\u53f0\u5f00\u53d1\">\u8de8\u5e73\u53f0\u5f00\u53d1<\/a> \u4e86\u89e3\u66f4\u591a\u3002<\/p>\n<\/blockquote>\n<h3>\u540c\u6b65\u8c03\u8bd5<\/h3>\n<p>\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c<code>Taro<\/code>\u4f1a\u628a\u5404\u7aef\u6253\u5305\u540e\u7684\u6587\u4ef6\u90fd\u653e\u5728<code>dist<\/code>\u76ee\u5f55\u3002\u5982\u679c\u60f3\u8981\u591a\u7aef\u540c\u6b65\u8c03\u8bd5\u7684\u8bdd\u5148\u7f16\u8bd1\u540e\u7684\u6587\u4ef6\u5c31\u4f1a\u88ab\u540e\u7f16\u8bd1\u597d\u7684\u6587\u4ef6\u8986\u76d6\u3002<\/p>\n<p>\u4f46\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u4fee\u6539\u7f16\u8bd1\u914d\u7f6e\u7684<code>outputRoot<\/code>\u8fbe\u5230\u591a\u7aef\u540c\u6b65\u8c03\u8bd5\u7684\u76ee\u7684\uff1a<\/p>\n<p>config\/index.js<\/p>\n<pre><code class=\"language-javascript\">const config = {\n  outputRoot: `dist\/${process.env.TARO_ENV}`\n}<\/code><\/pre>\n<p>\u5728\u8fd9\u6837\u7684\u914d\u7f6e\u4e0b\uff0c\u5fae\u4fe1\u5c0f\u7a0b\u5e8f\u7f16\u8bd1\u540e\u7684\u76ee\u5f55\u5c31\u4f1a\u662f<code>dist\/weapp<\/code>\uff0cH5 \u7f16\u8bd1\u540e\u76ee\u5f55\u5c31\u4f1a\u662f<code>dist\/h5<\/code>\u3002<\/p>\n<h3>\u4f7f\u7528\u539f\u751f\u5c0f\u7a0b\u5e8f\u7ec4\u4ef6<\/h3>\n<p>\u67d0\u4e9b\u60c5\u51b5\u4e0b\u6211\u4eec\u9700\u8981\u590d\u7528\u5c0f\u7a0b\u5e8f\u65e2\u6709\u751f\u6001\uff0c\u800c\u5c0f\u7a0b\u5e8f\u7684\u7ec4\u4ef6\/\u5e93\u901a\u5e38\u662f\u9488\u5bf9\u7279\u5b9a\u5c0f\u7a0b\u5e8f\u5199\u7684\uff0c\u5e76\u4e0d\u80fd\u76f4\u63a5\u5728<code>Taro<\/code>\u4e0a\u4f7f\u7528\uff0c\u9700\u8981\u4e00\u4e9b\u989d\u5916\u7684\u64cd\u4f5c\u3002<\/p>\n<p>\u4f8b\u5982\u6211\u4eec\u7684\u8bba\u575b\u5e94\u7528\uff0c\u5728\u5e16\u5b50\u8be6\u60c5\u53ef\u80fd\u670d\u52a1\u7aef\u8fd4\u56de\u7684\u662f MarkDown \u683c\u5f0f\uff0c\u6211\u4eec\u5c31\u9700\u8981<code>towxml<\/code>\u6765\u6e32\u67d3\u7684\u6211\u4eec\u7684\u5e16\u5b50\uff0c\u53c2\u8003\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/sbfkcel\/towxml\">https:\/\/github.com\/sbfkcel\/towxml<\/a><\/p>\n<p>\u9996\u5148\u6211\u4eec\u9700\u8981\u5728\u5e16\u5b50\u8be6\u60c5\u9875\u9762\u7684\u914d\u7f6e\u6587\u4ef6\u4e2d\u5f15\u7528<code>towxml<\/code>\uff1a<\/p>\n<pre><code class=\"language-javascript\">export default {\n  &quot;usingComponents&quot;: {\n    &quot;towxml&quot;:&quot;..\/..\/towxml\/towxml&quot;\n  }\n}<\/code><\/pre>\n<p>\u7136\u540e\u4f7f\u7528<code>towxml<\/code>\u7ec4\u4ef6\uff0c\u8fd9\u91cc\u5fc5\u987b\u8bb0\u4f4f\u7684\u662f\u4e0d\u7ba1\u662f React \u8fd8\u662f Vue\uff0c\u539f\u751f\u5c0f\u7a0b\u5e8f\u7ec4\u4ef6\u58f0\u660e\u9700\u8981\u662f\u5c0f\u5199\u7684<\/p>\n<p>React\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.jsx<\/code><\/p>\n<pre><code class=\"language-javascript\">- &lt;View dangerouslySetInnerHTML={{ __html: reply.content }} className=&#039;content&#039;&gt;&lt;\/View&gt;\n+ &lt;towxml nodes=&quot;{{reply.content}}&quot; \/&gt;<\/code><\/pre>\n<p>Vue\u7248\u672c\uff1a<code>src\/pages\/thread_detail\/thread_detail.vue<\/code><\/p>\n<pre><code class=\"language-javascript\">- &lt;view v-html=&quot;reply.content_rendered | html&quot; class=&#039;content&#039; \/&gt;\n+ &lt;towxml :nodes=&quot;reply.content_rendered | html&quot; \/&gt;<\/code><\/pre>\n<p>\u6700\u540e\u6309\u7167 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/sbfkcel\/towxml\/wiki\/3.0-\u5982\u4f55\u4f7f\u7528\" title=\"towxml\">towxml<\/a> \u6587\u6863\u8c03\u7528\u5373\u53ef\u3002<\/p>\n<blockquote>\n<p>\u4e00\u65e6\u4f7f\u7528\u4e86\u539f\u751f\u5c0f\u7a0b\u5e8f\u7ec4\u4ef6\uff0c<code>Taro<\/code>\u5e94\u7528\u5c31\u5931\u53bb\u4e86\u8de8\u7aef\u7684\u80fd\u529b\u3002<\/p>\n<p><code>Taro<\/code>\u8fd8\u652f\u6301\u4f7f\u7528\u5c0f\u7a0b\u5e8f\u63d2\u4ef6\uff0c\u8be6\u60c5\u53ef\u4ee5\u8bbf\u95ee\u6587\u6863 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/taro-docs.jd.com\/taro\/docs\/mini-third-party\" title=\"\u4f7f\u7528\u5c0f\u7a0b\u5e8f\u539f\u751f\u7b2c\u4e09\u65b9\u7ec4\u4ef6\u548c\u63d2\u4ef6\">\u4f7f\u7528\u5c0f\u7a0b\u5e8f\u539f\u751f\u7b2c\u4e09\u65b9\u7ec4\u4ef6\u548c\u63d2\u4ef6<\/a>\u3002<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\u57fa\u7840\u6559\u7a0b \u5b89\u88c5\u597dTaro CLI\u4e4b\u540e\u53ef\u4ee5\u901a\u8fc7taro init\u547d\u4ee4\u521b\u5efa\u4e00\u4e2a\u5168\u65b0\u7684\u9879\u76ee\uff0c\u4e00\u4e2a\u6700\u5c0f\u7248\u672c\u7684Taro\u9879 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[34],"tags":[328],"class_list":["post-1320","post","type-post","status-publish","format-standard","hentry","category-node","tag-taro"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1320","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1320"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1320\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1320"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1320"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1320"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}