tekton / components
一个组件系统,可以与gulp-single-file-components一起工作,也可以独立编译文件。
Requires
- php: >=7.0.0
- leafo/scssphp: ^0.7.7
- tekton/support: ^2.0.0
README
这是一个PHP组件库,有意构建为模块化,以便可以轻松集成到各种框架中。
安装
composer require tekton/components
JS编译器
如果您希望在构建过程中编译组件并且使用Node.js构建环境,您可以配置gulp-single-file-components来为您完成这项工作。不幸的是,由于该功能已集成到Tekton Components中,该项目不再积极维护。
用法
注册组件
ComponentManager和ComponentCompiler可以分别使用,并配置为提前编译文件并从缓存中加载,或者您可以配置一个编译器,在ComponentManager中自动使用,如果注册了单个文件组件文件(SCF)。
默认情况下,项目包括可以模拟Vue.js组件系统功能的过滤器和标签类。
use Tekton\Components\ComponentManager; use Tekton\Components\ComponentCompiler; $cacheDir = __DIR__.'/cache'; $compiler = new ComponentCompiler($cacheDir); $compiler->registerTags('template', new TemplateTag); $compiler->registerTags('style', new StyleTag); $compiler->registerTags('script', new ScriptTag); $manager = new ComponentManager($compiler); $manager->register('button.vue');
注册方法接受参数的其他方式如下
// Register by list of components where keys are the name to register $manager->register([ 'button' => [ 'template' => 'cache/button.html', 'style' => 'cache/button.css', 'script' => 'cache/button.js', ] // ... ]); // If non-associative array is passed then it must be component files and not // array of resources. The second argument can optionally be set to specify // the base directory so that components in sub-dirs don't create naming conflicts $manager->register([ 'button.vue', // will be named "button" 'contact/button.vue' // will be named "contact.button" // ... ], __DIR__); // Register by name and component instance $manager->register('button', new Component(['template' => 'cache/button.html'])); // Register by component path and optional base path $manager->register('components/button.vue', 'components/'); // Register component by name and resources array $manager->register('button', [ 'template' => 'cache/button.html', 'style' => 'cache/button.css', 'script' => 'cache/button.js', ]);
您可以通过具有名称和资源的关联数组检索编译器编译的所有组件,或者如果您有一个包含预编译组件的目录,您可以通过将文件扩展名与映射匹配来处理目录内容。
// From the compiler $components = $compiler->getComponentMap(); // Or $components = $manager->find('cache/', [ 'template' => ['html', 'php'], // Priority goes from end to beginning 'scripts' => 'js', 'style' => 'css', ]); // Passing (bool) true as the third argument registers them directly $manager->register($components);
例如,如果您只想在开发环境中测试文件更改,并在生产中避免列出文件,而是简单地在每个发布时清除缓存,则可以设置编译器在验证缓存时忽略文件修改时间,因此只有在文件之前未编译时才编译文件。
$compiler->setIgnoreCacheTime(true);
使用组件
要将组件包含在页面中,您只需调用。
$manager->include('button');
button.vue
<template lang="php"> <div class="button"> <?php if (true): ?> Button <?php endif; ?> </div> </template> <style lang="scss"> $myColor: #00ff00; .button { color: $myColor; } </style> <script> alert('component included') </script>
这样做将简单地渲染模板,CSS和JS需要在您的模板中单独处理,因为不同的框架处理资源的方式有很多。
// Combine all script files and only make one http request $cacheScripts = $cacheDir.'/components.js'; if (! file_exists($cacheScripts)) { $files = $manager->resources('script'); $combined = concat_files($files); file_put_contents($cacheScripts, $combined); } echo '<script src="'.$cacheScripts.'"></script>'; // You can validate your combined script by comparing modification time with... $mtime = $compiler->getLastCacheUpdate(); // Or include every file separately per request and only load those that have // been included in the page foreach ($manager->includedResources('script') as $name => $script) { echo '<script src="'.$script.'"></script>'; }
过滤器
过滤器在编译时运行在已注册的标签上,并可以使用标签属性确定是否应该运行(例如,在style标签上使用lang="scss")。它们被配置为在标签内容处理前后运行。要为style标签启用SCSS编译,您可以这样做
use Tekton\Components\ComponentManager; use Tekton\Components\ComponentCompiler; use Tekton\Components\Filters\ScssFilter; $styleTag = (new StyleTag)->addPostFilter(new ScssFilter); $cacheDir = __DIR__.'/cache'; $compiler = new ComponentCompiler($cacheDir); $compiler->registerTags('style', $styleTag); $manager = new ComponentManager($compiler);
作用域
为了避免样式和脚本冲突,您可以实现自己的作用域过滤器或使用内置的过滤器。StyleScope 将所有 CSS 规则以 ".component-button" 为前缀,而 TemplateScope 将模板包裹在元素中,并添加组件 ID 和 "component-button" 类。在模板内部,$this
总是指向组件实例,即使您没有使用 TemplateScope 过滤器,也可以轻松访问索引、名称和 ID。
use Tekton\Components\ComponentManager; use Tekton\Components\ComponentCompiler; use Tekton\Components\Tags\StyleTag; use Tekton\Components\Tags\ScriptTag; use Tekton\Components\Tags\TemplateTag; use Tekton\Components\Filters\StyleScope; use Tekton\Components\Filters\ScriptScope; use Tekton\Components\Filters\TemplateScope; $cacheDir = __DIR__.DS.'cache'; $compiler = new ComponentCompiler($cacheDir); $compiler->registerTags('template', (new TemplateTag)->addPostFilter(new TemplateScope)); $compiler->registerTags('style', (new StyleTag)->addPostFilter(new StyleScope)); $compiler->registerTags('script', (new ScriptTag)->addPostFilter(new ScriptScope)); $manager = new ComponentManager($compiler);
模板作用域支持类似 Emmet 的语法(div#myId.myclass[rel=myAttr]
),以配置自动创建的容器元素。它仅允许容器、ID、类和属性。这些中的任何一个都可以排除,但必须按此顺序。允许多个类和属性。
<template container="section[rel=home]" src="hero/full-page.html" />
结果将...
<section id="<?= $this->getId() ?>" class="component component-hero" rel="home"> <!-- contents of hero/full-page.html --> </section>
为了确保脚本在每个组件中只运行一次,或者如果它们都被编译到一个文件中,您需要一种方法来控制组件脚本仅在包含时执行。ScriptScope 过滤器为此提供了一些额外的辅助方法。这些必须在所有组件都已包含之后运行,因此,如果您正在使用从底部向上解析模板的模板系统,则应在 head 中运行,或者如果您在 body 的页脚中运行。
<!-- first include all the component scripts --> <script src="cache/compiled-scripts.js"></script> <script> // Pass manager to ScriptScope to create a list of all components included in the page <?= ScriptScope::getIncludedComponentsScript($manager); ?> // lastly include the script that handles execution <?= ScriptScope::getScopeScript(); ?> </script>
现在脚本标签的内容仅在包含时执行,并且每个组件执行一次。当它执行时,this
被设置为组件的包装元素。名称、选择器(类)和 ID 也会传递,以便您可以在包含多个组件的情况下仅处理当前实例的组件。
您还可以在脚本标签上设置 "singleton" 属性,以确保脚本无论包含多少组件都只执行一次。
自定义渲染器
为了将组件的渲染集成到不同的模板系统中,您很可能需要扩展组件类(实现 ComponentInterface)并更改 ComponentManager 使用的类来自动实例化组件。
// Either set it when creating the ComponentManager $manager = new ComponentManager($compiler, MyCustomComponent::class); // Or after $manager->setComponentClass(MyCustomComponent::class);
许可证
MIT