如何開(kāi)發(fā)一個(gè) Notadd Administration 模塊的前端擴(kuò)展

2018-05-23 12:04 更新

閱讀此文檔,需對(duì) Laravel,VueJS 2,Webpack 有了解。

前端擴(kuò)展,指的是,針對(duì)項(xiàng)目 notadd/administration 的前端部分進(jìn)行擴(kuò)展功能的開(kāi)發(fā)。

完整示例,請(qǐng)參考模塊項(xiàng)目 notadd/content 。

前端擴(kuò)展包含的功能注入點(diǎn)如下:

  • 擴(kuò)展安裝注入
  • 頭部菜單注入
  • 路由注入
  • 側(cè)邊欄菜單注入

說(shuō)明

項(xiàng)目 notadd/administration 的前端部分,是基于 VueJS 2 實(shí)現(xiàn)的單頁(yè)應(yīng)用(SPA)。

所以,對(duì)前端進(jìn)行擴(kuò)展,實(shí)際是對(duì) VueJS 項(xiàng)目的擴(kuò)展。

由于 VueJS 項(xiàng)目基于 Webpack 進(jìn)行構(gòu)建和打包,所以前端擴(kuò)展項(xiàng)目也必須基于 Webpack 進(jìn)行構(gòu)建和打包。

如何創(chuàng)建和開(kāi)發(fā) VueJS 2 的項(xiàng)目,請(qǐng)參見(jiàn) VueJS 官方文檔

但是,Notadd 的前端擴(kuò)展項(xiàng)目,并不是一個(gè)完整的 VueJS 2 的項(xiàng)目,因?yàn)?Notadd 只接受 UMD 模塊風(fēng)格的前端模塊注入,所以在使用 Webpack 進(jìn)行模塊構(gòu)建時(shí),webpackConfig 中需要針對(duì) output 參數(shù)進(jìn)行調(diào)整,主要體現(xiàn):

  • 必須定義 outputlibrary 別名,此名稱(chēng),必須與 捆綁 的模塊或擴(kuò)展項(xiàng)目中 composer.json 文件中定義的 name 完全一致,否則無(wú)法加載前端擴(kuò)展
  • 必須定義 outputlibraryTargetumd

配置代碼參考如下(來(lái)自文件 build/webpack.prod.conf.js):

var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')


var env = config.build.env


var webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/extension.js'),
    library: 'notadd/content',                                                              // 必須定義 library 別名
    libraryTarget: "umd"                                                                    // 必須定義 libraryTarget 為 umd
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].css')
    }),
    new OptimizeCSSPlugin()
  ]
})


if (config.build.productionGzip) {
  var CompressionWebpackPlugin = require('compression-webpack-plugin')


  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}


if (config.build.bundleAnalyzerReport) {
  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}


module.exports = webpackConfig

默認(rèn)導(dǎo)出模塊

使用 Webpack 導(dǎo)出 UMD 模塊風(fēng)格的模塊是,在 Webpack 配置中定義的 entry 入口文件中,必須使用默認(rèn)導(dǎo)出模塊,作為 Notadd 前端功能注入的注入點(diǎn)。

代碼參考如下:

```ecmascript 6 import {headerMixin, installMixin, routerMixin} from './helpers/mixes'

let Core = {}

headerMixin(Core) installMixin(Core) routerMixin(Core)

export default Core



如上代碼所示,模塊 Core 即為項(xiàng)目構(gòu)建后的默認(rèn)導(dǎo)出模塊,在該示例中,使用了 mixin 特性,為模塊增加 header,install,router的注入邏輯。


##擴(kuò)展安裝注入


如上(默認(rèn)導(dǎo)出模塊)述說(shuō),installMixin 為模塊 Core 注入了 Core.install 的實(shí)現(xiàn),具體代碼如下:


```ecmascript 6
export function installMixin (Core) {
  Core.install = function (Vue, Notadd) {
    Core.instance = Notadd
    vueMixin(Core, Vue)
  }
}
export function vueMixin (Core, Vue) {
  Core.http = Vue.http
}

Core.install 的調(diào)用者,為該方法提供了兩個(gè)對(duì)象,一個(gè)是 Vue 全局對(duì)象,一個(gè)是 Notadd 全局對(duì)象。

Vue 全局對(duì)象提供的特性,可以參考 VueJS 2 的官方文檔。

Notadd 全局對(duì)象主要包含如下特性:

  • Notadd.Vue:Vue 全局對(duì)象的副本
  • Notadd.http:axios 全局對(duì)象的副本
  • Notadd.store:Vuex 對(duì)象的副本
  • Notadd.components:常用的功能型組件(符合 Vue 組件規(guī)范)
  • Notadd.layouts:常用的布局型組件(符合 Vue 組件規(guī)范)

所以,如果模塊 Core 中需要使用 Vue 或 Notadd 的任意對(duì)象,均可通過(guò) mixin 特性來(lái)附加。

頭部菜單注入

如上(默認(rèn)導(dǎo)出模塊)述說(shuō),headerMixin 為模塊 Core 注入了 Core.header 的實(shí)現(xiàn),具體代碼如下:

```ecmascript 6 export function headerMixin (Core) { Core.header = function (menu) { menu.push({ 'text': '文章', 'icon': 'icon icon-article', 'uri': '/content' }) } }



##路由注入


如上(默認(rèn)導(dǎo)出模塊)述說(shuō),routerMixin 為模塊 Core 注入了 Core.router 的實(shí)現(xiàn),具體代碼如下:


```ecmascript 6
import ContentArticle from '../components/Article'
import ContentArticleCreate from '../components/ArticleCreate'
import ContentArticleDraft from '../components/ArticleDraft'
import ContentArticleDraftEdit from '../components/ArticleDraftEdit'
import ContentArticleEdit from '../components/ArticleEdit'
import ContentArticleRecycle from '../components/ArticleRecycle'
import ContentCategory from '../components/ArticleCategory'
import ContentComment from '../components/Comment'
import ContentComponent from '../components/Component'
import ContentDashboard from '../components/Dashboard'
import ContentExtension from '../components/Extension'
import ContentLayout from '../components/Layout'
import ContentPage from '../components/Page'
import ContentPageCategory from '../components/PageCategory'
import ContentPageCreate from '../components/PageCreate'
import ContentPageEdit from '../components/PageEdit'
import ContentTemplate from '../components/Template'
import ContentTag from '../components/ArticleTag'
export function routerMixin (Core) {
  Core.router = function (router) {
    router.modules.push({
      path: '/content',
      component: ContentLayout,
      children: [
        {
          path: '/',
          component: ContentDashboard,
          beforeEnter: router.auth
        },
        {
          path: 'article/all',
          component: ContentArticle,
          beforeEnter: router.auth
        },
        {
          path: 'article/create',
          component: ContentArticleCreate,
          beforeEnter: router.auth
        },
        {
          path: 'article/:id/draft',
          component: ContentArticleDraftEdit,
          beforeEnter: router.auth
        },
        {
          path: 'article/:id/edit',
          component: ContentArticleEdit,
          beforeEnter: router.auth
        },
        {
          path: 'article/category',
          component: ContentCategory,
          beforeEnter: router.auth
        },
        {
          path: 'article/tag',
          component: ContentTag,
          beforeEnter: router.auth
        },
        {
          path: 'article/recycle',
          component: ContentArticleRecycle,
          beforeEnter: router.auth
        },
        {
          path: 'article/draft',
          component: ContentArticleDraft,
          beforeEnter: router.auth
        },
        {
          path: 'page/all',
          component: ContentPage,
          beforeEnter: router.auth
        },
        {
          path: 'page/create',
          component: ContentPageCreate,
          beforeEnter: router.auth
        },
        {
          path: 'page/:id/edit',
          component: ContentPageEdit,
          beforeEnter: router.auth
        },
        {
          path: 'page/category',
          component: ContentPageCategory,
          beforeEnter: router.auth
        },
        {
          path: 'component',
          component: ContentComponent,
          beforeEnter: router.auth
        },
        {
          path: 'template',
          component: ContentTemplate,
          beforeEnter: router.auth
        },
        {
          path: 'extension',
          component: ContentExtension,
          beforeEnter: router.auth
        },
        {
          path: 'comment',
          component: ContentComment,
          beforeEnter: router.auth
        }
      ]
    })
  }
}

Core.router 的調(diào)用者,為該方法提供了一個(gè) router 對(duì)象,該 router 對(duì)象中包含如下特性:

  • auth: 后臺(tái)登錄驗(yàn)證中間件
  • bases: 基礎(chǔ)路由定義
  • modules: 模塊路由定義

側(cè)邊欄菜單注入

側(cè)邊欄菜單注入,提供了擴(kuò)展管理子級(jí)菜單的注入,由 Core.sidebar 提供注入,代碼參考如下:

```ecmascript 6 export default { sidebar: function (sidebar) { sidebar.push({ text: '多說(shuō)評(píng)論', icon: 'fa fa-comment', uri: '/duoshuo' }) } }



##前端擴(kuò)展構(gòu)建和打包


在進(jìn)行代碼編寫(xiě)和相關(guān)配置之后,使用命令 npm run build 即可完成對(duì)擴(kuò)展模塊的打包。


##前端資源注入


通過(guò)前端工具構(gòu)建和打包后,可以得到前端靜態(tài)資源文件(js文件,css文件,圖片文件等),可以模塊中的類(lèi) ModuleServiceProvider 或擴(kuò)展中的類(lèi) Extension 中將靜態(tài)資源文件發(fā)布到 public 目錄下。


類(lèi) ModuleServiceProvider 的代碼參考如下:


```php
<?php
/**
 * This file is part of Notadd.
 *
 * @author TwilRoad <269044570@qq.com>
 * @copyright (c) 2016, notadd.com
 * @datetime 2016-10-08 17:12
 */
namespace Notadd\Content;


use Illuminate\Support\ServiceProvider;


/**
 * Class Module.
 */
class ModuleServiceProvider extends ServiceProvider
{
    /**
     * Boot service provider.
     */
    public function boot()
    {
        $this->publishes([
            realpath(__DIR__ . '/../resources/mixes/administration/dist/assets/content/administration') => public_path('assets/content/administration'),
            realpath(__DIR__ . '/../resources/mixes/foreground/dist/assets/content/foreground') => public_path('assets/content/foreground'),
        ], 'public');
    }
}

然而,這樣并沒(méi)有結(jié)束,仍然需要告訴 Administration 模塊你提供了哪些靜態(tài)資源文件,給后臺(tái)的前端頁(yè)面使用。

在模塊中的類(lèi) ModuleServiceProvider 或擴(kuò)展中的類(lèi) Extension 中提供了相應(yīng)注入點(diǎn),script 方法將告訴后臺(tái)的前端頁(yè)面引用前面打包生成的 UMD 模塊文件,stylesheet 方法將告訴后臺(tái)的前端頁(yè)面引用前面打包生成樣式文件。

具體代碼參考如下:

<?php
/**
 * This file is part of Notadd.
 *
 * @author TwilRoad <269044570@qq.com>
 * @copyright (c) 2016, notadd.com
 * @datetime 2016-10-08 17:12
 */
namespace Notadd\Content;


use Illuminate\Support\ServiceProvider;


/**
 * Class Module.
 */
class ModuleServiceProvider extends ServiceProvider
{
    /**
     * Boot service provider.
     */
    public function boot()
    {
    }
    /**
     * Get script of extension.
     *
     * @return string
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public static function script()
    {
        return asset('assets/content/administration/js/module.js');
    }


    /**
     * Get stylesheet of extension.
     *
     * @return array
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public static function stylesheet()
    {
        return [
            asset('assets/content/administration/css/module.css'),
        ];
    }
}
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)