npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

san-loader

v0.3.7

Published

San single-file loader for Webpack

Downloads

2,559

Readme

San-Loader

San-Loader 是基于 webpack 的工具,允许开发者书写 San 单文件的方式来进行组件开发。

<template>
    <div class="content">Hello {{name}}!</div>
</template>

<script>
    export default {
        initData() {
            return {
                name: 'San'
            };
        }
    };
</script>

<style>
    .content {
        color: blue;
    }
</style>

San 单文件在写法上与 Vue 类似,San-Loader 会将 templatescriptstyle 等标签块当中的内容和属性提取出来,并交给 webpack 分别进行处理。最终单文件对外返回的将是一个普通的 San 组件类,我们可以直接使用它进行 San 组件的各种操作:

import App from './App.san';
let app = new App();
app.attach(document.body);

使用方法

通过 npm 进行 San-Loader 的安装:

npm install --save-dev san-loader

然后在 webpack 的配置文件上增加一条规则应用到 .san 文件上,并且增加一个 SanLoaderPlugin:

const SanLoaderPlugin = require('san-loader/lib/plugin');

module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.san$/,
                loader: 'san-loader'
            }
            // ...
        ]
    },
    plugins: [new SanLoaderPlugin()]
};

如前面提到,San-Loader 会将单文件的各个部分拆分出来,并交给其他的 Loader 来进行资源处理,因此还需要配置各个模块的处理方法,比如:

const SanLoaderPlugin = require('san-loader/lib/plugin');

module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.san$/,
                loader: 'san-loader'
            },
            {
                test: /\.js$/,
                loader: 'babel-loader'
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.html$/,
                loader: 'html-loader'
            }
            // ...
        ]
    },
    plugins: [new SanLoaderPlugin()]
};

在默认情况下,templatescriptstyle 会分别采用 .html.js.css 所对应的 Loader 配置进行处理,当然我们也可以在相应的标签上添加 lang 属性来指定不同的语言处理比如:

<style lang="less">
    @grey: #999;

    div {
        span {
            color: @grey;
        }
    }
</style>

这样,对应的样式模块就可以当成 .less 文件进行处理,只需要配置上相应的 Loader 即可。

// ...
module.exports = {
    // ...
    module: {
        rules: [
            // ...
            {
                test: /\.less$/,
                use: ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
};

更加完整的 webpack 配置,可以参考示例:

Options

| Name | Type | Default | Description | | :---------------: | :------------------------: | :------: | :------------------------------------------------------------------------ | | compileTemplate | {'none'|'aPack'|'aNode'} | 'none' | 将组件的template 编译成aPackaNode默认不编译,详细见下面说明 | | esModule | {Boolean} | false | san-loader 默认使用 CommonJS 模块语法来生成 JS 模块,将该参数设为 true 可以改用 ES 模块语法 | | autoAddScriptTag | {Boolean} | true | .san 文件中不含 script 标签时自动添加,默认为true |

特殊说明:

compileTemplate:San 组件的string类型的template通过编译可以返回aNode结构,在定义组件的时候,可以直接使用aNode作为 template,这样可以减少了组件的template编译时间,提升了代码的执行效率,但是转成aNode的组件代码相对来说比较大,所以在[email protected]引入的概念的aNode压缩结构aPack使用aPack可以兼顾体积和效率的问题。san-loader 中的compileTemplate就是来指定要不要将组件编译为aPack/aNode如果只想,单文件使用compileTemplate编译成对应的aPack或者aNode,可以直接在template上面写:<template compileTemplate="aPack">。 使用 pug 等预处理模版语言时,compileTemplate 不生效,请使用 san-anode-loader

扩展阅读

单文件写法

template

单文件中 template 模块的主要作用是提供一种更为便捷的方式来书写组件的 template 字符串。在配置 webpack 的时候,需要对 template 部分配置 raw-loader、html-loader 等等。其中如果 template 当中需要使用到图片、字体文件,建议采用 html-loader 配合 url-loader 的形式完成相关配置。例如:

<template>
    <div>
        <img src="../assets/logo.png" />
    </div>
</template>

则需要在 webpack 配置文件当中增加如下配置:

module.exports = {
    module: {
        rules: [
            // ...
            {
                test: /\.html$/,
                loader: 'html-loader'
            },
            {
                test: /\.png$/,
                loader: 'url-loader'
            }
        ]
    }
};

template 部分可以省略不写,直接在 script 模块当中定义也是可以的:

<script>
    export default {
        template: '<div>{{name}}</div>'
    };
</script>

template 模块也支持通过 src 标签引入 template 文件:

<template src="./component-template.html"></template>

注意:html-loader 最新版本在生产环境(production)会默认开启minimize=true,会导致 san 解析 template 失败,所以使用 html-loader 的时候建议开启minimize=false

script

script 模块必须通过 export default 将组件的 JS 代码导出。在写法上,支持类似 Vue 的写法:

<script>
    export default {
        initData() {
            return {
                name: 'San'
            };
        }
    };
</script>

San-Loader 会自动为导出为普通对象的模块外部自动包上 san.defineComponent 使之成为真正的 San 组件。

import script from './App.san?san&type=script&lang=js';
import san from 'san';
// ...
export default san.defineComponent(script);

我们也可以通过 class 的方式:

<script>
    import san from 'san';
    export default class App extends san.Component {
        initData() {
            return {
                name: 'San'
            };
        }
    }
</script>

也可以配合 san-store 一起使用,比如:

<script>
    import san from 'san';
    import {store, connect} from 'san-store';
    import {builder} from 'san-update';

    // ...
    export default connect.san({
        name: 'user.name'
    })(
        san.defineComponent({
            // ...
        })
    );
</script>

总之在写法上与普通的 San 组件不存在太大区别,区别的地方只在于 template 和 style 的部分可以放到别的模块里进行书写。

当组件不依赖数据和计算的时候,script 块可以省略不写。

与 template 相似,script 模块也可以通过定义 src 属性导入相应的组件代码:

<script src="./component-script.js"></script>

在默认情况下,script 模块的内容会被当成 .js 文件进行处理,如改成 TypeScript 的话,可以通过在 script 标签上添加属性 lang="ts" 将该模块标记为 .ts 文件,然后自行在 webpack 配置文件当中添加对 .ts 文件的处理 Loader 即可:

<script lang="ts">
    // ...
</script>

这时候需要修改ts-loader配置:

{
    test: /\.ts$/,
    loader: 'ts-loader',
    options: { appendTsSuffixTo: [/\.san$/] }
}

或者babel-loader的配置:

{
    test: /\.ts$/,
    use: [
        {
            loader: 'babel-loader',
            options: {
                plugins: [
                    require.resolve('@babel/plugin-proposal-class-properties'),
                    require.resolve('san-hot-loader/lib/babel-plugin')
                ],
                presets: [
                    [
                        require.resolve('@babel/preset-env'),
                        {
                            targets: {
                                browsers: '> 1%, last 2 versions'
                            },
                            modules: false
                        }

                    ],
                    // 下面配置 allExtensions
                    [require.resolve('@babel/preset-typescript'), {allExtensions: true}]
                ]
            }
        }
    ]
}

style

style 模块用来书写组件的样式,在用法上与 template、script 类似,例如:

<style>
    .parent {
        color: red;
    }

    .parent .children {
        color: green;
    }
</style>

在默认情况下,style 模块的内容会被当成 .css 文件处理,我们可以通过修改 lang 属性来指定文件类型。同时与 template、script 存在区别的地方在于,style 模块允许写多个,因此下面写的 style 模块都是有效的,全都会作用到当前的组件上:

<template><!-- 组件模板  --></template>
<script>
    /* 组件 script */
</script>
<style>
    /* 写普通 css */
    .parent .children {
        color: green;
    }
</style>

<style lang="less">
    /* 写 less */
    @grey: #999;
    .parent {
        .children {
            background: @grey;
        }
    }
</style>
<!-- 引入外部 stylus 样式文件 -->
<style src="./component-style.styl"></style>

CSS Modules

基本使用

CSS Modules 是一个流行的用于模块化和组合 CSS 的系统,san-loader 提供了与 css-loader 的集成以支持 CSS Modules 的特性。在模板中可以这样写:

<template>
    <div class="{{$style.wrapper}}"></div>
</template>

<script>
    export default {
        attached() {
            let style = this.data.get('$style');
            console.log(style);
        }
    };
</script>

<style module>
    .wrapper {
        color: black;
    }
</style>

如果要对所有文件生效,在上面的 webpack 配置示例中给 css-loader 添加 modules 参数即可。例如:

// webpack.config.js 省略上下文
rules: [
    {
        test: /\.css$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    modules: {
                        localIdentName: '[local]_[hash:base64:5]'
                    },
                    localsConvention: 'camelCase',
                    sourceMap: true
                }
            }
        ]
    }
];

其中 localIdentName 用来指定编译后的类名,在开发环境请使用 '[hash:base64]'localsConvention 是在模板和 JavaScript 中引用的名称,默认是不转换,'camelCase' 是把类名转换为驼峰风格。详情请参考:css-loader 文档

命名 CSS Modules

默认通过<style module></style>方式添加的样式,在组件中会给data添加$style变量。如果组件中有多个style,想区分不同的 style,也可以通过命名的方式定义不同style的变量名,示例如下:

<template>
    <div class="{{$style}}">
        <div class="{{$styleFooter}}"></div>
    </div>
</template>

<style module>
</style>

<style module="styleFooter">
</style>

允许非 CSS Modules

也可以指定部分 style 标签使用 CSS Modules,其他仍然是普通的全局 CSS:

<style module>
    /* 这里是 CSS Modules */
</style>

<style>
    /* 这里是全局 CSS */
</style>

san-loader 会给带 module<style> 添加对应的 resourceQuery,所以你可以这样配置:

// webpack.config.js 省略上下文
rules: [
    {
        test: /\.css$/,
        oneOf: [
            // 这里匹配 `<style module>`
            {
                resourceQuery: /module/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: {
                                localIdentName: '[local]_[hash:base64:5]'
                            },
                            localsConvention: 'camelCase',
                            sourceMap: true
                        }
                    }
                ]
            },
            // 这里匹配 `<style>`
            {
                use: [
                    {
                        loader: 'style-loader'
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            }
        ]
    }
];

和预处理器一起使用

你也可以把 CSS Modules 和 LESS 等预处理器一起使用,添加对应的 loader 即可。比如:

// webpack.config.js 省略上下文
rules: [
    {
        test: /\.less$/,
        oneOf: [
            // 这里匹配 `<style lang="less" module>`
            {
                resourceQuery: /module/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: {
                                localIdentName: '[local]_[hash:base64:5]'
                            },
                            localsConvention: 'camelCase',
                            sourceMap: true
                        }
                    },
                    {
                        loader: 'less-loader',
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            }
            // 这里匹配 `<style lang="less">`
            // ...
        ]
    }
];

一些有用的用例

CSS Modules 可以在使用 slot 时使用(会被编译到随机的类名):

<template>
    <div>
        <child-component>
            <span class="{{$style.bold}}">foo</span>
        </child-component>
    </div>
</template>

<style module>
    .bold {
        font-weight: bold;
    }
</style>

也可以设置子组件的根元素样式(会被正确编译到随机类名):

<template>
    <div>
        <child-component class="child"></child-component>
    </div>
</template>

<style module>
    .child {
        font-weight: bold;
    }
</style>

但父组件无法覆盖子组件的内部类的样式,比如子组件内存在类名 .foo,父组件里的 .child .foo 不会渗透进入子组件:

<template>
    <div>
        <child-component class="child"></child-component>
    </div>
</template>

<style module>
    .child .foo {
        font-weight: bold;
    }
</style>

但除类名之外的元素名、ID 等会渗透进入子组件,例如下面的 .child span 会作用于 <child-component> 里的 <span>

<template>
    <div>
        <child-component class="child"></child-component>
    </div>
</template>

<style module>
    .child span {
        font-weight: bold;
    }
</style>

Scoped CSS(version 0.3.0 以上)

你可以在 <style> 标签上添加 scoped 属性,此时标签内的 CSS 只作用于当前组件 template 中的元素。编译后的 html 会添加 data-s-${hash} 属性。举例:

<template>
    <div>
        <h1>red</h1>
    </div>
</template>

<style scoped>
    h1 {
        color: red;
    }
</style>

浏览器中会表现为

...
<head>
    <style>
        h1[data-s-2dad60b2] {
            color: red;
        }
    </style>
</head>

<body>
    <h1>normal black</h1>
    ...
    <div data-s-2dad60b2>
        <h1 data-s-2dad60b2>red</h1>
    </div>
</body>