整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          vue的兩種服務器端渲染方案

          vue的兩種服務器端渲染方案

          者:京東零售 姜欣

          關于服務器端渲染方案,之前只接觸了基于react的Next.js,最近業務開發vue用的比較多,所以調研了一下vue的服務器端渲染方案。 首先:長文預警,下文包括了兩種方案的實踐,沒有耐心的小伙伴可以直接跳到方案標題下,down代碼體驗一下。

          前置知識:

          1、什么是服務器端渲染(ssr)?

          簡單來說就是用戶第一次請求頁面時,頁面上的內容是通過服務器端渲染生成的,瀏覽器直接顯示服務端返回的完整html就可以,加快首屏顯示速度。

          舉個栗子:

          當我們訪問一個商品列表時,如果使用客戶端渲染(csr),瀏覽器會加載空白的頁面,然后下載js文件,通過js在客戶端請求數據并渲染頁面。如果使用服務器端渲染(ssr),在請求商品列表頁面時,服務器會獲取所需數據并將渲染后的HTML發送給瀏覽器,瀏覽器一步到位直接展示,而不用等待數據加載和渲染,提高用戶的首屏體驗。

          2、服務器端渲染的優缺點

          優點:

          (1)更好的seo:抓取工具可以直接查看完全渲染的頁面。現在比較常用的交互是頁面初始展示 loading 菊花圖,然后通過異步請求獲取內容,但是但抓取工具并不會等待異步完成后再行抓取頁面內容。

          (2)內容到達更快:不用等待所有的 js 都完成下載并執行,所以用戶會更快速地看到完整渲染的頁面。

          缺點:

          (1)服務器渲染應用程序,需要處于 Node.js server 運行環境

          (2)開發成本比較高

          總結:

          總得來說,決定是否使用服務器端渲染,取決于具體的業務場景和需求。對于具有大量靜態內容的簡單頁面,客戶端渲染更合適一些,因為它可以更快地加載頁面。但是對于需要從服務器動態加載數據的復雜頁面,服務器端渲染可能是一個更好的選擇,因為他可以提高用戶的首屏體驗和搜索引擎優化。


          下面進入正文

          方案一:vue插件vue-server-render

          git 示例demo地址

          結論前置:不建議用,配置成本高

          官網地址: https://v2.ssr.vuejs.org/zh/

          首先要吐槽一下官網,按官網教程比較難搞,目錄安排的不太合理,一頓操作項目都沒起來...

          并且官網示例的構建配置代碼是webpack4的,現在初始化項目后基本安裝的都是webpack5,有一些語法不同

          (1)首先,先初始化一個npm項目,然后安裝依賴得到一個基礎項目 。(此處要注意vue-server-renderer 和 vue 必須匹配版本)

          npm init -y
          yarn add vue vue-server-renderer -S
          yarn add express -S
          yarn add webpack webpack-cli friendly-errors-webpack-plugin vue-loader babel-loader @babel/core url-loader file-loader vue-style-loader css-loader sass-loader sass webpack-merge webpack-node-externals -D
          yarn add clean-webpack-plugin @babel/preset-env -D
          yarn add rimraf // 模擬linx的刪除命令,在build時先刪除dist
          yarn add webpack-dev-middleware webpack-hot-middleware -D
          yarn add chokidar  -D //監聽變化
          yarn add memory-fs -D
          yarn add nodemon -D
          ...實在太多,如有缺失可以在package.json中查找
          另外:我現在用的"vue-loader": "^15.9.0"版本,之前用的是"vue-loader": "^17.0.1",報了一個styles的錯

          (2)配置app.js,entry-client.js,entry-server.js,將官網參考中的示例代碼拷貝至對應文件。

          app.js

          import Vue from 'vue'
          import App from './App.vue'
          import { createRouter } from './router'
          import { createStore } from './store'
          import { sync } from 'vuex-router-sync'
          
          // 導出一個工廠函數,用于創建新的
          // 應用程序、router 和 store 實例
          export function createApp () {
              // 創建 router 和 store 實例
              const router=createRouter()
              const store=createStore()
              
              sync(store, router)
          
              const app=new Vue({
                  router,
                  store,
                  render: h=> h(App)
              })
          
              return { app, router, store }
          }
          
          

          entry-client.js

          import Vue from 'vue'
          import { createApp } from './app'
          
          Vue.mixin({
              beforeMount () {
                  const { asyncData }=this.$options
                  if (asyncData) {
                      this.dataPromise=asyncData({
                          store: this.$store,
                          route: this.$route
                      })
                  }
              }
          })
          
          const { app, router, store }=createApp()
          
          if (window.__INITIAL_STATE__) {
              store.replaceState(window.__INITIAL_STATE__)
          }
          
          router.onReady(()=> {
              // 在初始路由 resolve 后執行,
              // 使用 `router.beforeResolve()`,以便確保所有異步組件都 resolve。
              router.beforeResolve((to, from, next)=> {
                  const matched=router.getMatchedComponents(to)
                  const prevMatched=router.getMatchedComponents(from)
          
                  // 找出兩個匹配列表的差異組件
                  let diffed=false
                  const activated=matched.filter((c, i)=> {
                      return diffed || (diffed=(prevMatched[i] !==c))
                  })
          
                  if (!activated.length) {
                      return next()
                  }
          
                  Promise.all(activated.map(c=> {
                      if (c.asyncData) {
                          return c.asyncData({ store, route: to })
                      }
                  })).then(()=> {
                      next()
                  }).catch(next)
              })
          
              app.$mount('#app')
          })

          entry-server.js

          import { createApp } from './app'
          
          export default context=> {
              // 返回一個promise,服務器能夠等待所有的內容在渲染前,已經準備就緒,
              return new Promise((resolve, reject)=> {
                  const { app, router, store }=createApp()
                  
                  router.push(context.url)
          
                  router.onReady(()=> {
                      const matchedComponents=router.getMatchedComponents()
                      if (!matchedComponents.length) {
                          return reject({ code: 404 })
                      }
          
                      // 對所有匹配的路由組件調用 `asyncData()`
                      Promise.all(matchedComponents.map(Component=> {
                          if (Component.asyncData) {
                              return Component.asyncData({
                                  store,
                                  route: router.currentRoute
                              })
                          }
                      })).then(()=> {
                          context.state=store.state
          
                          resolve(app)
                      }).catch(reject)
                  }, reject)
              })
          }

          (3)在根目錄下創建server.js 文件

          其中一個非常重要的api:createBundleRenderer,這個api上面有一個方法renderToString將代碼轉化成html字符串,主要功能就是把用webpack把打包后的服務端代碼渲染出來。具體了解可看官網bundle renderer指引。

          // server.js
          const app=require('express')()
          const { createBundleRenderer }=require('vue-server-renderer')
          const fs=require('fs')
          const path=require('path')
          const resolve=file=> path.resolve(__dirname, file)
          
          const isProd=process.env.NODE_ENE==="production"
          
          const createRenderer=(bundle, options)=> {
              return createBundleRenderer(bundle, Object.assign(options, {
                  basedir: resolve('./dist'),
                  runInNewContext: false,
              }))
          }
          
          let renderer, readyPromise
          const templatePath=resolve('./src/index.template.html')
          if (isProd) {
              const bundle=require('./dist/vue-ssr-server-bundle.json')
              const clientManifest=require('./dist/vue-ssr-client-manifest.json')
              const template=fs.readFileSync(templatePath, 'utf-8')
          
              renderer=createRenderer(bundle, {
                  // 推薦
                  template, // (可選)頁面模板
                  clientManifest // (可選)客戶端構建 manifest
              })
          } else {
              // 開發模式
              readyPromise=require('./config/setup-dev-server')(app, templatePath, (bundle, options)=> {
                  renderer=createRenderer(bundle, options)
              })
          }
          
          const render=(req, res)=> {
              const context={
                  title: 'hello ssr with webpack',
                  meta: `
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <meta http-equiv="X-UA-Compatible" content="ie=edge">
              `,
                  url: req.url
              }
              renderer.renderToString(context, (err, html)=> {
                  if (err) {
                      if (err.code===404) {
                          res.status(404).end('Page not found')
                      } else {
                          res.status(500).end('Internal Server Error')
                      }
                  } else {
                      res.end(html)
                  }
              })
          }
          
          // 在服務器處理函數中……
          app.get('*', isProd ? render : (req, res)=> {
              readyPromise.then(()=> render(req, res))
          })
          
          app.listen(8080) // 監聽的是8080端口

          (4)接下來是config配置

          在根目錄新增config文件夾,然后新增四個配置文件:webpack.base.config,webpack.client.config,webpack.server.config,setup-dev-server(此方法是一個封裝,為了配置個熱加載,差點沒搞明白,參考了好多)(官網傳送門: 構建配置 )

          大部分官網有示例代碼,但是要在基礎上進行一些更改

          webpack.base.config

          // webpack.base.config
          const path=require('path')
          // 用來處理后綴為.vue的文件
          const { VueLoaderPlugin }=require('vue-loader')
          const FriendlyErrorsWebpackPlugin=require('friendly-errors-webpack-plugin')
          // 定位到根目錄
          const resolve=(dir)=> path.join(path.resolve(__dirname, "../"), dir)
          
          // 打包時會先清除一下
          // const { CleanWebpackPlugin }=require('clean-webpack-plugin')
          
          const isProd=process.env.NODE_ENV==="production"
          
          module.exports={
              mode: isProd ? 'production' : 'development',
              output: {
                  path: resolve('dist'),
                  publicPath: '/dist/',
                  filename: '[name].[chunk-hash].js'
              },
              resolve: {
                  alias: {
                      'public': resolve('public')
                  }
              },
              module: {
                  noParse: /es6-promise\.js$/,
                  rules: [
                      {
                          test: /\.vue$/,
                          loader: 'vue-loader',
                          options: {
                              compilerOptions: {
                                  preserveWhiteSpace: false
                              }
                          }
                      },
                      {
                          test: /\.js$/,
                          loader: 'babel-loader',
                          exclude: /node_modules/
                      },
                      {
                          test: /\.(png|jpg|gif|svg)$/,
                          loader: 'url-loader',
                          options: {
                              limit: 10000,
                              name: '[name].[ext]?[hash]'
                          }
                      },
                      {
                          test: /\.s(a|c)ss?$/,
                          use: ['vue-style-loader', 'css-loader', 'sass-loader']
                      }
                  ]
              },
              performance: {
                  hints: false
              },
              plugins:[
                  new VueLoaderPlugin(),
                  // 編譯后的友好提示,比如編譯完成或者編譯有錯誤
                  new FriendlyErrorsWebpackPlugin(),
                  // 打包時會先清除一下
                  // new CleanWebpackPlugin()
              ]
          }

          webpack.client.config

          // webpack.client.config
          const {merge}=require('webpack-merge')
          const baseConfig=require('./webpack.base.config.js')
          const VueSSRClientPlugin=require('vue-server-renderer/client-plugin')
          
          module.exports=merge(baseConfig, {
              entry: {
                  app: './src/entry-client.js'
              },
              optimization: {
                  // 重要信息:這將 webpack 運行時分離到一個引導 chunk 中,
                  // 以便可以在之后正確注入異步 chunk。
                  // 這也為你的 應用程序/vendor 代碼提供了更好的緩存。
                  splitChunks: {
                      name: "manifest",
                      minChunks: Infinity
                  }
              },
              plugins: [
                  // 此插件在輸出目錄中
                  // 生成 `vue-ssr-client-manifest.json`。
                  new VueSSRClientPlugin()
              ]
          })

          webpack.server.config

          // webpack.server.config
          const {merge}=require('webpack-merge')
          const nodeExternals=require('webpack-node-externals')
          
          // webpack的基礎配置,比如sass,less預編譯等
          const baseConfig=require('./webpack.base.config.js')
          const VueSSRServerPlugin=require('vue-server-renderer/server-plugin')
          
          module.exports=merge(baseConfig, {
              // 將 entry 指向應用程序的 server entry 文件
              entry: './src/entry-server.js',
          
              target: 'node',
          
              // 對 bundle renderer 提供 source map 支持
              devtool: 'source-map',
          
              // 此處告知 server bundle 使用 Node 風格導出模塊(Node-style exports)
              output: {
                  libraryTarget: 'commonjs2'
              },
          
              // https://webpack.js.org/configuration/externals/#function
              // https://github.com/liady/webpack-node-externals
              // 外置化應用程序依賴模塊。可以使服務器構建速度更快,
              // 并生成較小的 bundle 文件。
              externals: nodeExternals({
                  // 不要外置化 webpack 需要處理的依賴模塊。
                  // 你可以在這里添加更多的文件類型。例如,未處理 *.vue 原始文件,
                  // 你還應該將修改 `global`(例如 polyfill)的依賴模塊列入白名單
                  allowlist: /\.css$/
              }),
          
              // 這是將服務器的整個輸出
              // 構建為單個 JSON 文件的插件。
              // 默認文件名為 `vue-ssr-server-bundle.json`
              plugins: [
                  new VueSSRServerPlugin()
              ]
          })

          setup-dev-server:封裝createRenderer方法

          const webpack=require('webpack')
          const fs=require('fs')
          const path=require('path')
          const chokidar=require('chokidar')
          const middleware=require("webpack-dev-middleware")
          const HMR=require("webpack-hot-middleware")
          const MFS=require('memory-fs')
          
          const clientConfig=require('./webpack.client.config')
          const serverConfig=require('./webpack.server.config')
          
          const readFile=(fs, file)=> {
              try {
                  return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf8')
              } catch (error) {
          
              }
          }
          
          const setupServer=(app, templatePath, cb)=> {
              let bundle
              let clientManifest
              let template
              let ready
              const readyPromise=new Promise(r=> ready=r)
          
              template=fs.readFileSync(templatePath, 'utf8')
              const update=()=> {
                  if (bundle && clientManifest) {
                      // 通知 server 進行渲染
                      // 執行 createRenderer -> RenderToString
                      ready()
                      cb(bundle, {
                          template,
                          clientManifest
                      })
                  }
              }
              // webpack -> entry-server -> bundle
              const mfs=new MFS();
              const serverCompiler=webpack(serverConfig);
          
              serverCompiler.outputFileSystem=mfs;
              serverCompiler.watch({}, (err, stats)=> {
                  if (err) throw err
                  // 之后讀取輸出:
                  stats=stats.toJson()
                  stats.errors.forEach(err=> console.error(err))
                  stats.warnings.forEach(err=> console.warn(err))
                  if (stats.errors.length) return
                  bundle=JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
                  update()
              });
          
              clientConfig.plugins.push(
                  new webpack.HotModuleReplacementPlugin()
              )
              clientConfig.entry.app=['webpack-hot-middleware/client', clientConfig.entry.app]
              clientConfig.output.filename='[name].js'
          
              const clientCompiler=webpack(clientConfig);
          
              const devMiddleware=middleware(clientCompiler, {
                  noInfo: true, publicPath: clientConfig.output.publicPath, logLevel: 'silent'
              })
              app.use(devMiddleware);
          
              app.use(HMR(clientCompiler));
          
              clientCompiler.hooks.done.tap('clientsBuild', stats=> {
                  stats=stats.toJson()
                  stats.errors.forEach(err=> console.error(err))
                  stats.warnings.forEach(err=> console.warn(err))
                  if (stats.errors.length) return
                  clientManifest=JSON.parse(readFile(
                      devMiddleware.fileSystem,
                      'vue-ssr-client-manifest.json'
                  ))
                  update()
              })
          
              // fs -> templatePath -> template
              chokidar.watch(templatePath).on('change', ()=> {
                  template=fs.readFileSync(templatePath, 'utf8')
                  console.log('template is updated');
                  update()
              })
          
              return readyPromise
          }
          
          module.exports=setupServer

          (5)配置搞完了接下來是代碼渲染

          在src目錄下,新增index.template.html文件,將官網中的例子(地址:使用一個頁面模板 )復制,并進行一些更改

          <html>
          <head>
              <!-- 使用雙花括號(double-mustache)進行 HTML 轉義插值(HTML-escaped interpolation) -->
              <title>{{ title }}</title>
          
              <!-- 使用三花括號(triple-mustache)進行 HTML 不轉義插值(non-HTML-escaped interpolation) -->
              {{{ meta }}}
          </head>
          <body>
          <!--這個是告訴我們在哪里插入正文的內容-->
          <!--vue-ssr-outlet-->
          </body>
          </html>

          (6)再搞個store和api模擬一下數據請求

          這里介紹一下一個很重要的東西asyncData 預取數據,預取數據是在vue掛載前,所以下文這里用了上下文來獲取store而不是this

          asyncData: ({ store })=> { return store.dispatch('getDataAction') },

          在src下創建api文件夾,并在下面創建data.js文件

          // data.js
          const getData=()=> new Promise((resolve)=> {
              setTimeout(()=> {
                  resolve([
                      {
                          id: 1,
                          item: '測試1'
                      },
                      {
                          id: 2,
                          item: '測試2'
                      },
                  ])
              }, 1000)
          })
          
          export {
              getData
          }

          在src下創建store文件夾,并在下面創建index.js文件

          // store.js
          import Vue from 'vue'
          import Vuex from 'vuex'
          
          Vue.use(Vuex)
          
          import { getData } from '../api/data'
          
          export function createStore () {
              return new Vuex.Store({
                  state: {
                      lists: []
                  },
                  actions: {
                      getDataAction ({ commit }) {
                          return getData().then((res)=> {                   
                              commit('setData', res)
                          })
                      }
                  },
                  mutations: {
                      setData (state, data) {
                          state.lists=data
                      }
                  }
              })
          }

          (7)編寫組件,在src/components文件夾下寫兩個組件,在app.vue中引用一下,用上剛寫的模擬數據

          Hello.vue

          <template>
            <div>
              這里是測試頁面一
              <p>{{item}}</p>
              <router-link to="/hello1">鏈接到測試頁面二</router-link>
            </div>
          </template>
          
          <script>
          export default {
            asyncData: ({ store })=> {
              return store.dispatch('getDataAction')
            },
            computed: {
              item () {
                return this.$store.state.lists
              }
            }
          }
          </script>
          
          <style lang="scss" scoped>
          </style>

          Hello1.vue

          <template>
            <div>這里是測試頁面二{{item}}</div>
          </template>
          
          <script>
          export default {
            asyncData: ({ store })=> {
              return store.dispatch('getDataAction')
            },
            computed: {
              item () {
                return this.$store.state.lists
              }
            }
          }
          </script>
          
          <style lang="scss" scoped>
          </style>

          (8)配置路由并在app.vue使用路由

          router.js

          import Vue from 'vue'
          import Router from 'vue-router'
          
          Vue.use(Router)
          
          export function createRouter () {
              return new Router({
                  mode: 'history',
                  routes: [
                      {
                          path: '/hello',
                          component: ()=> import('./components/Hello.vue')
                      },
                      {
                          path: '/hello1',
                          component: ()=> import('./components/Hello1.vue')
                      },
                  ]
              })
          } 

          app.vue

          <template>
            <div id="app">
              <router-view></router-view>
            </div>
          </template>
          
          <script>
          
          export default {
            name: 'App',
          }
          </script>
          
          <style lang="scss" scoped>
          </style>

          (9)根目錄下創建一個.babelrc,進行配置

          {
            "presets": [
              [
                "@babel/preset-env",
                {
                  "modules": false
                }
              ]
            ]
          }

          (10)改寫package.json執行命令

          "dev": "nodemon server.js",
          "build": "rimraf dist && npm run build:client && npm run build:server",
          "build:client": "webpack --config config/webpack.client.config.js",
          "build:server": "webpack --config config/webpack.server.config.js"

          大搞告成,執行一下dev命令,可以通過訪問localhost:8080端口看到頁面,記得帶上路由哦~

          執行build命令可看到,最后dist文件下共有三個文件:main.[chunk-hash].js,vue-ssr-client-manifest.json,vue-ssr-server-bundle.json

          附上文件整體目錄結構


          ??

          方案二:基于vue的nuxt.js通用應用框架

          git 示例demo地址

          一對比,這個就顯得絲滑多了~ 官網地址: nuxt.js

          先對比一下兩種方案的差別

          1.vue初始化雖然有cli,但是nuxt.js的cli更加完備
          2.nuxt有更合理的工程化目錄,vue過于簡潔,比如一些component,api文件夾都是要手動創建的
          3.路由配置:傳統應用需要自己來配置,nuxt.js自動生成
          4.沒有統一配置,需手動創建。nuxt.js會生成nuxt.config.js
          5.傳統不易與管理底層框架邏輯(nuxt支持中間件管理,雖然我還沒探索過這里)

          顯而易見這個上手就快多了,也不需要安裝一大堆依賴,如果用了sass需要安裝sass和sass-loader,反正我是用了

          (1)創建一個項目 可選npm,npx,yarn,具體看官方文檔

          npm init nuxt-app <project-name>

          (2)pages下面創建幾個文件

          nuxt是通過pages頁面形成動態的路由,不用手動配置路由。比如在pages下面新增了個文件about.vue,那么這個頁面對應的路由就是/about

          其實這個時候運行npm run dev 就可以看到簡單的頁面了

          (3)模擬接口

          這里介紹一個插件,可以快速創建一個服務

          npm i json-server 

          安裝完后,在根目錄新增db.json文件,模擬幾個接口

          {
            "post": [{"id": 1, "title": "json-server", "author": "jx"}],
            "comments": [{"id": 1, "body": "some comment", "postId": 1}],
            "profile": {"name": "typicode"}
          }

          運行命令json-server --watch db.json --port=8000(不加會端口沖突),就可以看到


          ??

          因為是get請求,可以直接點擊訪問可以看到mock的數據已經返回了


          ??

          (4)頁面調用

          先配置一下axios,推薦使用nuxt.js封裝的axios:"@nuxtjs/axios": "^5.13.6",然后再在nuxt.config.js文件中modules下面配置一下就可以使用了

          modules: [  '@nuxtjs/axios'],

          隨便找個接口調用一下

          <template>
            <div>
              <div>
                這里是測試頁面一
              </div>
              接口返回數據:{{posts}}
            </div>
          </template>
          
          <script>
          export default {
            name: 'IndexPage',
            async asyncData({$axios}){
              const result=await $axios.get('http://localhost:8000/post')
              return{
                posts: result.data
              }
            }
          }
          </script>

          刷新下頁面就可以看到效果了,這里注意$axios有兩個get方法,一個$axios.get還會返回頭部等信息,另一個$axios.$get只返回結果


          總結:

          從頁面篇幅上應該也能看到哪個容易上手了,nuxt相對于插件來說限定了文件夾的結構,并通過此預定了一些功能,更好上手。預設了利用vue.js開發服務端渲染所需要的各種配置,并且提供了提供了靜態站點,異步數據加載,中間件支持,布局支持等

          一篇文章講解“重定向”,本篇文章講解“視圖-模板渲染”。


          因為新版的控制器可以無需繼承任何的基礎類,因此在控制器中如何使用視圖取決于你怎么定義控制器。

          渲染模板最常用的是控制器類在繼承系統控制器基類(\think\Controller)后調用fetch方法,調用格式:

          模板文件的寫法支持下面幾種:


          1. 基本使用

          下面是一個最典型的用法,不帶任何參數:

          ①新建Index控制器,并在控制器中新建index方法

          注意:

          1. 不帶任何參數,表示系統會按照默認規則自動定位模板文件,其規則是:

          當前模塊/view/當前控制器名(小寫)/當前操作(小寫).html

          ②在index/view/index/下新建index.html模板文件

          預覽:


          2. 修改模板引擎的view_depr(模板文件名分隔符)設置

          ①修改模板引擎的view_depr為“_”

          位置:配置項文件config.php的template參數下。

          注意:

          1. 如果有更改模板引擎的view_depr設置(假設'view_depr'=>'_')的話,則上面的自動定位規則變成:

          當前模塊/view/當前控制器(小寫)_當前操作(小寫).html

          ②在index/view下新建index_index.html模板文件

          訪問Index控制器下的index方法,預覽:


          3. 如果沒有按照模板定義規則來定義模板文件(或者需要調用其他控制器下面的某個模板)

          1)沒有按照模板定義規則來定義模板文件

          【例】在Index控制器的add方法中要調用同控制器下的edit.html模板

          ①Index控制器的add方法

          注意:

          1. $this->fetch(‘edit’);表示調用當前控制器的edit模板。

          ②在index/view/index/下新建edit.html模板(view_depr設置為默認)。

          訪問add方法,預覽:

          2)調用其他控制器下的某個模板

          【例】在Index控制器的edit方法下,調用News控制器下的add模板。

          ①Index控制器下的edit方法:

          ②在index/view/news下新建add.html模板。

          訪問Index控制器的edit方法,預覽:


          4. 跨模塊渲染模板

          【例】在index模塊的Index控制器的del方法中,調用admin模塊User控制器下的index.html模板。

          ①index模塊的Index控制器的del方法

          ②admin模塊的User控制器下的index.html模板

          訪問index模塊的Index的del方法,預覽:

          注意:

          1. 渲染輸出不需要寫模板文件的路徑和后綴。

          2. 這里面的控制器和操作并不一定需要有實際對應的控制器和操作,只是一個目錄名稱和文件名稱而已。

          【例2】你的項目里面可能根本沒有Public控制器,更沒有Public控制器的menu操作,但是一樣可以使用。

          ①在Index控制器中新建pub方法,調用Public控制器的menu模板

          ②在index/view/public下新建menu.html模板

          訪問Index控制器的pub方法,預覽:

          5. 從視圖根目錄開始讀取模板

          支持從視圖根目錄開始讀取模板,即從view文件夾下讀取。

          ①在Index控制器中新建viewFloder方法,調用視圖根目錄下的模板。

          ②在index/view下新建folder.html模板

          訪問Index控制器的viewFloder方法,預覽:

          注意:

          1. 如果需要調用視圖類(think\View)的其它方法,可以直接使用$this->view對象。

          【例】調用視圖類的config方法(配置模板引擎),設置模板后綴。

          視圖類(think\View)的config方法:

          默認的模板后綴:

          在Index方法中,新建viewTest方法

          訪問viewTest方法,預覽:

          注意:

          1. 在視圖根目錄下有folder.html模板,當設置模板后綴為.htm時,那么將不會訪問folder.html模板,將會訪問folder.htm模板。


          6. 自定義模板文件位置

          如果你的模板文件位置比較特殊或者需要自定義模板文件的位置,可以采用下面的方式處理。

          ①在Index控制器中新建Custom方法,調用自定義的模板文件位置

          ../template/index/是相當于當前項目入口文件的位置。

          ②在項目下創建template/index文件夾,并新建custom.html模板

          預覽:

          注意:

          1. 這種方式需要帶模板路徑和后綴指定一個完整的模板文件位置,這里的../template/index目錄是相對于當前項目入口文件位置。

          2. 如果是其他的后綴文件,也支持直接輸出,例如:

          return $this->fetch('../template/public/menu.tpl');

          只要../template/index/menu.tpl是一個實際存在的模板文件。

          3. 要注意模板文件位置是相對于應用的入口文件,而不是模板目錄。

          ThinkPHP5連載為卓象程序員原創,轉載請聯系卓象程序員

          關注卓象程序員,定期發布技術文章

          下一篇講解“視圖-助手函數+渲染內容”

          年來,隨著互聯網的高速發展,網頁中蘊藏的海量數據成為各行業競爭的關鍵。而在獲取這些數據的過程中,一個重要的技術就是爬蟲。結合JavaScript渲染技術,爬蟲不僅能夠更好地應對動態網頁,還能為用戶帶來更加精準、豐富的信息。下面小編將為您詳細介紹這一新興技術。

          一、爬蟲與JavaScript渲染的概念

          爬蟲是一種自動化程序,可以模擬人類對網頁進行訪問,并從中提取所需信息。而JavaScript渲染則是指網頁加載時,瀏覽器會執行其中的JavaScript代碼,生成并顯示頁面內容。傳統的爬蟲技術只能抓取靜態網頁內容,無法獲取通過JavaScript動態生成的信息。而結合爬蟲和JavaScript渲染技術,則可以解決這一問題。

          二、爬蟲與JavaScript渲染技術的優勢

          1.動態網頁抓取:傳統爬蟲只能抓取靜態頁面,無法獲取動態生成的內容。而結合JavaScript渲染技術,爬蟲可以模擬瀏覽器行為,獲取完整的網頁數據。

          2.數據準確性:JavaScript渲染技術可以實時加載和更新數據,爬蟲可以及時抓取最新的信息,保證數據的準確性。

          3.用戶體驗優化:通過爬蟲和JavaScript渲染技術,網頁內容可以更加豐富、多樣化,提升用戶體驗。

          4.數據分析與挖掘:爬蟲獲取到的數據可以用于各種分析和挖掘工作,為企業決策提供參考。

          三、爬蟲與JavaScript渲染的應用案例

          1.電商行業:通過爬蟲和JavaScript渲染技術,企業可以實時抓取競爭對手的商品信息、價格變動等數據,從而制定更具競爭力的銷售策略。

          2.新聞媒體:通過爬蟲和JavaScript渲染技術,新聞媒體可以快速抓取各類資訊,并實時更新到自己的平臺上,提供給用戶最新、全面的新聞內容。

          3.社交媒體監測:通過爬蟲和JavaScript渲染技術,企業可以實時監測社交媒體上與自身品牌相關的討論和用戶反饋,及時做出回應和調整。

          4.數據分析與預測:通過爬蟲和JavaScript渲染技術,可以獲取大量的數據,進行數據分析和挖掘,為企業決策提供可靠的依據。

          四、注意事項與發展趨勢

          1.合法合規:在使用爬蟲技術時,需要遵守相關法律法規,尊重網站的使用規則,避免侵犯他人的權益。

          2.技術更新迭代:隨著互聯網技術的不斷發展,爬蟲和JavaScript渲染技術也在不斷更新迭代中。開發者需要及時了解新技術,并靈活應用于實際項目中。

          總結:

          爬蟲與JavaScript渲染技術的結合為我們帶來了更多可能性。無論是從數據獲取、用戶體驗還是商業決策等方面,這一技術都具有重要意義。然而,在應用過程中,我們也要遵守相關規定,保護他人的權益。相信隨著技術的不斷發展,爬蟲與JavaScript渲染將會在更多領域展現出其強大的潛力和價值。


          主站蜘蛛池模板: 麻豆AV一区二区三区久久| 精品无码人妻一区二区三区品| 无码aⅴ精品一区二区三区| 无码毛片一区二区三区中文字幕| 自慰无码一区二区三区| 亚洲综合av一区二区三区| 91国在线啪精品一区| 日本免费精品一区二区三区| 亚州AV综合色区无码一区| 香蕉免费一区二区三区| 精品国产不卡一区二区三区 | 精品久久久久中文字幕一区| 中文字幕在线不卡一区二区| 日韩精品一区二区三区四区 | 国产精品一区三区| 在线免费视频一区二区| 麻豆AV无码精品一区二区| 久久国产精品视频一区| 另类免费视频一区二区在线观看| 国产情侣一区二区三区 | 国产人妖视频一区二区| 久久亚洲中文字幕精品一区四| 精品女同一区二区三区在线 | 亚洲制服中文字幕第一区| 伊人激情AV一区二区三区| 中文字幕在线视频一区| 中文字幕Av一区乱码| 午夜天堂一区人妻| 精品一区精品二区制服| 亚洲视频一区在线| 亚洲一本一道一区二区三区| 午夜爽爽性刺激一区二区视频| 国产AV午夜精品一区二区入口| 国产成人精品视频一区二区不卡| 99国产精品一区二区| 久久99精品一区二区三区| 一区二区三区在线|欧| 国产精品久久一区二区三区| 亚洲日韩中文字幕一区| 国产剧情一区二区| 亚洲国产高清在线一区二区三区 |