閱讀此文件,需對 Laravel,VueJS 2,Webpack 有了解。
前端擴充套件,指的是,針對專案 notadd/administration 的前端部分進行擴充套件功能的開發。
完整示例,請參考模組專案 notadd/content 。
前端擴充套件包含的功能注入點如下:
- 擴充套件安裝注入
- 頭部選單注入
- 路由注入
- 側邊欄選單注入
說明
專案 notadd/administration 的前端部分,是基於 VueJS 2 實現的單頁應用(SPA)。
所以,對前端進行擴充套件,實際是對 VueJS 專案的擴充套件。
由於 VueJS 專案基於 Webpack 進行構建和打包,所以前端擴充套件專案也必須基於 Webpack 進行構建和打包。
如何建立和開發 VueJS 2 的專案,請參見 VueJS 官方文件。
但是,Notadd 的前端擴充套件專案,並不是一個完整的 VueJS 2 的專案,因為 Notadd 只接受 UMD 模組風格的前端模組注入,所以在使用 Webpack 進行模組構建時,webpackConfig 中需要針對 output 引數進行調整,主要體現:
- 必須定義 output 的 library 別名,此名稱,必須與 捆綁 的模組或擴充套件專案中 composer.json 檔案中定義的 name 完全一致,否則無法載入前端擴充套件
- 必須定義 output 的 libraryTarget 為 umd
配置程式碼參考如下(來自檔案 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
預設匯出模組
使用 Webpack 匯出 UMD 模組風格的模組是,在 Webpack 配置中定義的 entry 入口檔案中,必須使用預設匯出模組,作為 Notadd 前端功能注入的注入點。
程式碼參考如下:
import {headerMixin, installMixin, routerMixin} from './helpers/mixes'
let Core = {}
headerMixin(Core)
installMixin(Core)
routerMixin(Core)
export default Core
如上程式碼所示,模組 Core 即為專案構建後的預設匯出模組,在該示例中,使用了 mixin 特性,為模組增加 header,install,router的注入邏輯。
擴充套件安裝注入
如上(預設匯出模組)述說,installMixin 為模組 Core 注入了 Core.install 的實現,具體程式碼如下:
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 的呼叫者,為該方法提供了兩個物件,一個是 Vue 全域性物件,一個是 Notadd 全域性物件。
Vue 全域性物件提供的特性,可以參考 VueJS 2 的官方文件。
Notadd 全域性物件主要包含如下特性:
- Notadd.Vue:Vue 全域性物件的副本
- Notadd.http:axios 全域性物件的副本
- Notadd.store:Vuex 物件的副本
- Notadd.components:常用的功能型元件(符合 Vue 元件規範)
- Notadd.layouts:常用的佈局型元件(符合 Vue 元件規範)
所以,如果模組 Core 中需要使用 Vue 或 Notadd 的任意物件,均可透過 mixin 特性來附加。
頭部選單注入
如上(預設匯出模組)述說,headerMixin 為模組 Core 注入了 Core.header 的實現,具體程式碼如下:
export function headerMixin (Core) {
Core.header = function (menu) {
menu.push({
'text': '文章',
'icon': 'icon icon-article',
'uri': '/content'
})
}
}
路由注入
如上(預設匯出模組)述說,routerMixin 為模組 Core 注入了 Core.router 的實現,具體程式碼如下:
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 的呼叫者,為該方法提供了一個 router 物件,該 router 物件中包含如下特性:
- auth: 後臺登入驗證中介軟體
- bases: 基礎路由定義
- modules: 模組路由定義
側邊欄選單注入
側邊欄選單注入,提供了擴充套件管理子級選單的注入,由 Core.sidebar 提供注入,程式碼參考如下:
export default {
sidebar: function (sidebar) {
sidebar.push({
text: '多說評論',
icon: 'fa fa-comment',
uri: '/duoshuo'
})
}
}
前端擴充套件構建和打包
在進行程式碼編寫和相關配置之後,使用命令 npm run build 即可完成對擴充套件模組的打包。
前端資源注入
透過前端工具構建和打包後,可以得到前端靜態資原始檔(js檔案,css檔案,圖片檔案等),可以模組中的類 ModuleServiceProvider 或擴充套件中的類 Extension 中將靜態資原始檔釋出到 public 目錄下。
類 ModuleServiceProvider 的程式碼參考如下:
<?php
/**
* This file is part of Notadd.
*
* @author TwilRoad <269044570@qq.com>
* @copyright (c) 2016, iBenchu.org
* @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');
}
}
然而,這樣並沒有結束,仍然需要告訴 Administration 模組你提供了哪些靜態資原始檔,給後臺的前端頁面使用。
在模組中的類 ModuleServiceProvider 或擴充套件中的類 Extension 中提供了相應注入點,script 方法將告訴後臺的前端頁面引用前面打包生成的 UMD 模組檔案,stylesheet 方法將告訴後臺的前端頁面引用前面打包生成樣式檔案。
具體程式碼參考如下:
<?php
/**
* This file is part of Notadd.
*
* @author TwilRoad <269044570@qq.com>
* @copyright (c) 2016, iBenchu.org
* @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'),
];
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結