【大前端之前后分離02】前端模板嵌套問題


回顧

接上文:【大前端之前后分離01】JS前端渲染VS服務器端渲染,我們探討了為什么要做前后分離,以及前端渲染需要解決的問題,最后提出了自己的解決方案:

前端代碼編譯形成兩套代碼:①前端發布版本 + ②服務器端腳本

這個想法借鑒了fis plus的smarty模塊化思維,以及reactJS編譯運行的概念,上次初步論證了其可行性,也遺留了一些問題,其中比較關鍵的問題是:

前端模塊嵌套問題

我們在一個模板中又有一個widget,在子模板中又有一個widget,父模塊與子模塊中有數據依賴,或者子模塊為一個循環,循環卻依賴父模塊某個值,這個便很麻煩。

舉個例子來說,我們首頁引入了一個商品模塊,商品類型模塊為一循環模塊,里面又有子模塊:

index首頁模塊:

1 <div id="type_widget_wrapper">
2   <script type="text/javascript">
3     render('text!./template/type.html', './model/type', './controller/type', 'type_widget_wrapper');
4   </script>
5 </div>

type模塊:

 1 <ul id="type_id">
 2     <% for (var i = 0, len = data.length; i < len; i++) { %>
 3     <li class="type js_type">
 4         <h2><%=data[i].name%></h2>
 5         <ul class="product_list">
 6             <% for (var j = 0, len1 = data[i].product.length; j < len1; j++) { %>
 7                 <li class="product">
 8                     <%=data[i].product[j].name%>
 9                 </li>
10             <% } %>
11         </ul>
12     </li>
13     <% } %>
14 </ul>

可以看到,其中有第二次循環迭代的將該類型的商品信息讀出,如果我們想將商品信息模塊化的,這里便出現了模塊嵌套情況:

 1 <ul id="type_id">
 2     <% for (var i = 0, len = data.length; i < len; i++) { %>
 3     <li class="type js_type">
 4         <h2><%=data[i].name%></h2>
 5         <ul class="product_list">
 6         <div id="product_list_widget_wrapper">
 7           <script type="text/javascript">
 8             render('text!./template/product_list.html', './model/product_list', './controller/product_list', 'product_list_widget_wrapper');
 9           </script>
10         </div>
11         </ul>
12     </li>
13     <% } %>
14 </ul>

這里暫時不考慮子模塊中還有異步數據請求問題,我們將列表對應的模板放到了單個文件中:

1 <% for (var j = 0, len1 = data[i].product.length; j < len1; j++) { %>
2     <li class="product">
3         <%=data[i].product[j].name%>
4     </li>
5 <% } %>

這里的循環解析便是我們今天研究的重點,因為前端模塊至少需要兩個條件:

① 唯一的dom容器

② 能獲取父級模塊的相關數據

為了解決這個問題,我這里提出了迭代模塊的概念。

迭代模塊

所謂迭代模塊,便是用於數據內嵌形式,並且處於循環中的模塊,比如上述例子,我整個type模板就變成了這樣(這里為最簡形式):

 1 <ul id="type_id">
 2   <% for (var i = 0, len = data.length; i < len; i++) { %>
 3   <li class="type js_type">
 4     <h2>
 5       <%=data[i].name%></h2>
 6     <ul class="product_list">
 7 
 8       <div id="data_inner_widget_wrapper_<%=i %>">
 9         <script type="text/javascript">
10           iteratorRender({
11             index: typeof <%=i%> == 'string' ? '<%=i%>' : <%=i%>,
12             value: <%=JSON.stringify(data[i])%>,
13             name: 'data_inner'
14           });
15         </script>
16       </div>
17 
18     </ul>
19   </li>
20   <% } %>
21 </ul>

這個是編譯過后形成的前端代碼,最初是這樣的:

 1 <ul id="type_id">
 2   <% for (var i = 0, len = data.length; i < len; i++) { %>
 3   <li class="type js_type">
 4     <h2>
 5       <%=data[i].name%></h2>
 6     <ul class="product_list">
 7         <%iteratorWidget({
 8           index: <%=i%>,
 9           value: <%=JSON.stringify(data[i])%>,
10           name: 'data_inner',
11         }); %>
12     </ul>
13   </li>
14   <% } %>
15 </ul>
1 <%iteratorWidget({
2   index: <%=i%>, //索引,整數或者字符串
3   value: <%=JSON.stringify(data[i])%>, //對應數據對象,字符串或者json對象
4   name: 'data_inner',
5 }); %>

這個時候前端需要實現iteratorRender方法,首先前端模板將上述代碼解析結束后是這個樣子的:

 1 "<ul id="type_id">
 2   
 3   <li class="type js_type">
 4     <h2>
 5       電腦</h2>
 6     <ul class="product_list">
 7 
 8       <div id="data_inner_widget_wrapper_0">
 9         <script type="text/javascript">
10           iteratorRender({
11             index: typeof 0 == 'string' ? '0' : 0,
12             value: {"id":1,"name":"電腦","product":[{"name":"戴爾"},{"name":"蘋果"},{"name":"聯想"},{"name":"華碩"}]},
13             name: 'data_inner'
14           });
15         </script>
16       </div>
17 
18     </ul>
19   </li>
20   
21   <li class="type js_type">
22     <h2>
23       書籍</h2>
24     <ul class="product_list">
25 
26       <div id="data_inner_widget_wrapper_1">
27         <script type="text/javascript">
28           iteratorRender({
29             index: typeof 1 == 'string' ? '1' : 1,
30             value: {"id":2,"name":"書籍","product":[{"name":"三國演義"},{"name":"西游記"},{"name":"紅樓夢"},{"name":"水滸傳"}]},
31             name: 'data_inner'
32           });
33         </script>
34       </div>
35 
36     </ul>
37   </li>
38   
39   <li class="type js_type">
40     <h2>
41       游戲</h2>
42     <ul class="product_list">
43 
44       <div id="data_inner_widget_wrapper_2">
45         <script type="text/javascript">
46           iteratorRender({
47             index: typeof 2 == 'string' ? '2' : 2,
48             value: {"id":3,"name":"游戲","product":[{"name":"仙劍1"},{"name":"仙劍2"},{"name":"仙劍3"},{"name":"仙劍4"}]},
49             name: 'data_inner'
50           });
51         </script>
52       </div>
53 
54     </ul>
55   </li>
56   
57 </ul>
View Code
 1 <li class="type js_type">
 2   <h2>
 3     電腦</h2>
 4   <ul class="product_list">
 5 
 6     <div id="data_inner_widget_wrapper_0">
 7       <script type="text/javascript">
 8         iteratorRender({
 9           index: typeof 0 == 'string' ? '0' : 0,
10           value: { "id": 1, "name": "電腦", "product": [{ "name": "戴爾" }, { "name": "蘋果" }, { "name": "聯想" }, { "name": "華碩"}] },
11           name: 'data_inner'
12         });
13       </script>
14     </div>
15 
16   </ul>
17 </li>

然后前端方法的實現為:

 1 //最簡單實現,僅考慮渲染,不嚴謹
 2 var iteratorRender = function (opts) {
 3   var name = opts.name;
 4   var index = opts.index;
 5   var data = typeof opts.value == 'string' ? JSON.parse(opts.value) : opts.value;
 6   var wrapperId = name + '_widget_wrapper_' + index;
 7   var template = 'text!./template/' + name + '.html';
 8   var controller = './controller/' + name;
 9 
10   require([template, controller], function (tpl, view) {
11     var html = $(_.template(tpl)(data));
12     var wrapper = $('#' + wrapperId);
13     html.insertBefore(wrapper);
14     wrapper.remove();
15     //執行控制器
16     view.init();
17   });
18 }

然后代碼運行,邏輯跑通了:

結語

由於最近工作強度上來了,解決了前端渲染時候的模板嵌套問題,一直拖到了今天,服務器端的模板嵌套更好處理,該方案后續會繼續細化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM