首页

SpringBoot与Web开发

前端达人

SpringBoot与Web开发(超详细)

一、简介

二、SpringBoot对静态资源的映射规则

1、所有 /webjars/ ,都去 classpath:/META-INF/resources/webjars/ 找资源

2、"/
" 访问当前项目的任何资源,都去静态资源的文件夹找映射

3、欢迎页: 静态资源文件夹下的所有index.html页面,被"/"映射

三、模板引擎

1、引入Thymeleaf

2、Thymeleaf的使用

1、导入thymeleaf的名称空间

2、使用thymeleaf语法

3、Thymeleaf的语法规则

四、SpringMVC自动配置

1、Spring MVC auto-configuration

2、扩展SpringMVC

原理

3、全面接管SpringMVC

原理

五、如何修改SpringBoot的默认配置

一、简介

使用SpringBoot的步骤:



1、创建SpringBoot应用,选中我们需要的模块。

2、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来。

3、自己编写业务代码。



自动配置原理:



xxxxAutoConfiguration:帮我们给容器中自动配置组件

xxxxProperties:配置类来封装配置文件的内容

1

2

二、SpringBoot对静态资源的映射规则

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)

public class ResourceProperties implements ResourceLoaderAware {

  //可以设置和静态资源有关的参数,缓存时间等

1

2

3

WebMvcAuotConfiguration:

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

if (!this.resourceProperties.isAddMappings()) {

logger.debug("Default resource handling disabled");

return;

}

Integer cachePeriod = this.resourceProperties.getCachePeriod();

if (!registry.hasMappingForPattern("/webjars/
")) {

customizeResourceHandlerRegistration(

registry.addResourceHandler("/webjars/**")

.addResourceLocations(

"classpath:/META-INF/resources/webjars/")

.setCachePeriod(cachePeriod));

}

String staticPathPattern = this.mvcProperties.getStaticPathPattern();

          //静态资源文件夹映射

if (!registry.hasMappingForPattern(staticPathPattern)) {

customizeResourceHandlerRegistration(

registry.addResourceHandler(staticPathPattern)

.addResourceLocations(

this.resourceProperties.getStaticLocations())

.setCachePeriod(cachePeriod));

}

}



       //配置欢迎页映射

@Bean

public WelcomePageHandlerMapping welcomePageHandlerMapping(

ResourceProperties resourceProperties) {

return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),

this.mvcProperties.getStaticPathPattern());

}



      //配置喜欢的图标

@Configuration

@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)

public static class FaviconConfiguration {



private final ResourceProperties resourceProperties;



public FaviconConfiguration(ResourceProperties resourceProperties) {

this.resourceProperties = resourceProperties;

}



@Bean

public SimpleUrlHandlerMapping faviconHandlerMapping() {

SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();

mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);

              //所有  /favicon.ico 

mapping.setUrlMap(Collections.singletonMap("
/favicon.ico",

faviconRequestHandler()));

return mapping;

}



@Bean

public ResourceHttpRequestHandler faviconRequestHandler() {

ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();

requestHandler

.setLocations(this.resourceProperties.getFaviconLocations());

return requestHandler;

}



}





1、所有 /webjars/ ,都去 classpath:/META-INF/resources/webjars/ 找资源

webjars:以jar包的方式引入静态资源。WebJars



访问localhost:8080/webjars/jquery/3.3.1/jquery.js的结果:





2、"/
" 访问当前项目的任何资源,都去静态资源的文件夹找映射

"classpath:/META-INF/resources/", 

"classpath:/resources/",

"classpath:/static/", 

"classpath:/public/" 

"/":当前项目的根路径



例子:访问localhost:8080/abc 就是去静态资源文件夹里面找abc



例如我们访问js文件夹下的Chart.min.js:



访问结果:





3、欢迎页: 静态资源文件夹下的所有index.html页面,被"/"映射

编写index.html文件。



访问结果:





三、模板引擎

常见的模板引擎:JSP、Velocity、Freemarker、Thymeleaf(springboot推荐,语法更简单,功能更强大)





1、引入Thymeleaf

Thymeleaf官网



在pom.xml中添加以下依赖:



 <dependency>

   <groupId>org.springframework.boot</groupId>

     <artifactId>spring-boot-starter-thymeleaf</artifactId>

 </dependency>



2、Thymeleaf的使用

@ConfigurationProperties(prefix = "spring.thymeleaf")

public class ThymeleafProperties {



private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");



private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");



public static final String DEFAULT_PREFIX = "classpath:/templates/";



public static final String DEFAULT_SUFFIX = ".html";



1

只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染。



success.html:



HelloController:



package com.keafmd.springboot.controller;



import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;



/


  Keafmd

 


  @ClassName: HelloController

 
@Description:

  @author: 牛哄哄的柯南

 
@date: 2021-03-04 19:54

 */



@Controller

public class HelloController {



    @ResponseBody

    @RequestMapping("/hello")

    public String hello(){

        return "Hello World";

    }



    @RequestMapping("/success")

    public String success() {

        return "success";

    }

}



访问success的结果:





1、导入thymeleaf的名称空间

<html lang="en" xmlns:th="http://www.thymeleaf.org"&gt;

1

2、使用thymeleaf语法

HelloController:



package com.keafmd.springboot.controller;



import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;



import java.util.Map;



/*

 
Keafmd

 

 
@ClassName: HelloController

  @Description:

 
@author: 牛哄哄的柯南

  @date: 2021-03-04 19:54

 
/



@Controller

public class HelloController {



    @ResponseBody

    @RequestMapping("/hello")

    public String hello(){

        return "Hello World";

    }



    /*

     
查出一些数据在页面显示

      @param map

     
@return

     */

    @RequestMapping("/success")

    public String success(Map<String,Object> map) {

        map.put("hello","你好");

        return "success";

    }

}



success.html:



<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.org"&gt;

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

    <h1>成功</h1>

    <!--th:text 将div里面的文本内容设置为-->

    <div th:text="${hello}"></div>

</body>

</html>



运行结果:





3、Thymeleaf的语法规则

1、th:任意html属性,来替换原生属性的值



th:text — 改变当前元素里面的文本内容

更多参考下图:



2、表达式



Simple expressions:(表达式语法)

    Variable Expressions: ${...}:获取变量值;OGNL;

    1)、获取对象的属性、调用方法

    2)、使用内置的基本对象:

    #ctx : the context object.

    #vars: the context variables.

                #locale : the context locale.

                #request : (only in Web Contexts) the HttpServletRequest object.

                #response : (only in Web Contexts) the HttpServletResponse object.

                #session : (only in Web Contexts) the HttpSession object.

                #servletContext : (only in Web Contexts) the ServletContext object.

                

                ${session.foo}

            3)、内置的一些工具对象:

execInfo : information about the template being processed.

messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.

uris : methods for escaping parts of URLs/URIs

conversions : methods for executing the configured conversion service (if any).

dates : methods for java.util.Date objects: formatting, component extraction, etc.

calendars : analogous to #dates , but for java.util.Calendar objects.

numbers : methods for formatting numeric objects.

strings : methods for String objects: contains, startsWith, prepending/appending, etc.

objects : methods for objects in general.

bools : methods for boolean evaluation.

arrays : methods for arrays.

lists : methods for lists.

sets : methods for sets.

maps : methods for maps.

aggregates : methods for creating aggregates on arrays or collections.

ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).



    Selection Variable Expressions: {...}:选择表达式:和${}在功能上是一样;

    补充:配合 th:object="${session.user}:

   <div th:object="${session.user}">

    <p>Name: <span th:text="
{firstName}">Sebastian</span>.</p>

    <p>Surname: <span th:text="{lastName}">Pepper</span>.</p>

    <p>Nationality: <span th:text="
{nationality}">Saturn</span>.</p>

    </div>

    

    Message Expressions: #{...}:获取国际化内容

    Link URL Expressions: @{...}:定义URL;

    @{/order/process(execId=${execId},execType='FAST')}

    Fragment Expressions: ~{...}:片段引用表达式

    <div th:insert="~{commons :: main}">...</div>

   

Literals(字面量)

      Text literals: 'one text' , 'Another one!' ,…

      Number literals: 0 , 34 , 3.0 , 12.3 ,…

      Boolean literals: true , false

      Null literal: null

      Literal tokens: one , sometext , main ,…

Text operations:(文本操作)

    String concatenation: +

    Literal substitutions: |The name is ${name}|

Arithmetic operations:(数学运算)

    Binary operators: + , - , * , / , %

    Minus sign (unary operator): -

Boolean operations:(布尔运算)

    Binary operators: and , or

    Boolean negation (unary operator): ! , not

Comparisons and equality:(比较运算)

    Comparators: > , < , >= , <= ( gt , lt , ge , le )

    Equality operators: == , != ( eq , ne )

Conditional operators:条件运算(三元运算符)

    If-then: (if) ? (then)

    If-then-else: (if) ? (then) : (else)

    Default: (value) ?: (defaultvalue)

Special tokens:

    No-Operation: _ 



注意:内容过多,详细内容参考官方文档。



示例:↓



HelloController:



package com.keafmd.springboot.controller;



import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;



import java.util.Arrays;

import java.util.Map;



/*

 
Keafmd

 

 
@ClassName: HelloController

  @Description:

 
@author: 牛哄哄的柯南

  @date: 2021-03-04 19:54

 
/



@Controller

public class HelloController {



    @ResponseBody

    @RequestMapping("/hello")

    public String hello(){

        return "Hello World";

    }



    /*

     
查出一些数据在页面显示

      @param map

     
@return

     */

    @RequestMapping("/success")

    public String success(Map<String,Object> map) {

        map.put("hello","你好");

        map.put("hello1","<h1>你好</h1>");

        map.put("users", Arrays.asList("柯南","小兰","基德"));

        return "success";

    }

}



success.html:



<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.org"&gt;

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

    <h1>成功</h1>

    <!--th:text 将div里面的文本内容设置为-->

    <div id="div01" class="myDiv" th:id="${hello}" th:class="${hello}" th:text="${hello}">这里的内容被覆盖</div>



    <hr/>

    <div th:text="${hello1}"></div>

    <div th:utext="${hello1}"></div>

    <hr/>

    <!--th:each 每次遍历都会生成当前这个标签-->

    <h4 th:text="${user}" th:each="user:${users}"></h4>

    <hr/>

    <h4>

        <span th:each="user:${users}"> [[${user}]] </span>

    </h4>

</body>

</html>



效果:







四、SpringMVC自动配置

1、Spring MVC auto-configuration

参考官方文档:点这里



Spring Boot 自动配置好了SpringMVC



以下是SpringBoot对SpringMVC的默认配置:(WebMvcAutoConfiguration)



Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.



自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))

ContentNegotiatingViewResolver:组合所有的视图解析器的。

如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来。

Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars



Static index.html support. 静态首页访问



Custom Favicon support (see below). favicon.ico



自动注册了 of Converter, GenericConverter, Formatter beans.



Converter:转换器; public String hello(User user):类型转换使用Converter

Formatter :格式化器; 2017.12.17===Date

@Bean

@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则

public Formatter<Date> dateFormatter() {

    return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件

}

1

2

3

4

5

自己添加的格式化器转换器,我们只需要放在容器中即可



Support for HttpMessageConverters (see below).



HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User—Json



HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter



自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)



Automatic registration of MessageCodesResolver (see below):定义错误代码生成规则



Automatic use of a ConfigurableWebBindingInitializer bean (see below).



我们可以配置一个ConfigurableWebBindingInitializer来替换默认的(添加到容器)



初始化WebDataBinder

请求数据=====JavaBean

1

2

org.springframework.boot.autoconfigure.web:web的所有自动场景



If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.



如果你想保持Spring Boot MVC 功能,你只是想添加额外的(MVC配置)(https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/htmlsingle MVC)(拦截器,格式器,视图控制器等)您可以添加自己的@ configuration类WebMvcConfigurerAdapter类型,但没有@EnableWebMvc。如果你想提供RequestMappingHandlerMapping, RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,你可以声明一个WebMvcRegistrationsAdapter实例来提供这样的组件。



If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.



如果你想完全控制Spring MVC,你可以添加你自己的@Configuration注解@EnableWebMvc。



2、扩展SpringMVC

实现如下功能:



<mvc:view-controller path="/hello" view-name="success"></mvc:view-controller>



<mvc:interceptors>

    <mvc:interceptor>

        <mvc:mapping path="/hello"/>

        <bean></bean>

    </mvc:interceptor>

</mvc:interceptors>



做法:编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc



特点:既保留了所有的自动配置,也能用我们扩展的配置。



在config包下创建个MyMvcConfig。



代码实现:



package com.keafmd.springboot.config;



import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;





/*

 
Keafmd

 

 
@ClassName: MyMvcConfig

  @Description:

 
@author: 牛哄哄的柯南

  @date: 2021-03-17 20:26

 
/

@Configuration

public class MyMvcConfig implements WebMvcConfigurer {

    @Override

    public void addViewControllers(ViewControllerRegistry registry) {

        //浏览器发送 /keafmd 请求 来到success页面

        registry.addViewController("/keafmd").setViewName("success");

    }

}



原理

1、WebMvcAutoConfiguration是SpringMVC的自动配置类。

2、在做其他自动配置时会导入,@Import(EnableWebMvcConfiguration.class)。



   @Configuration

public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

     private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();



     //从容器中获取所有的WebMvcConfigurer

     @Autowired(required = false)

     public void setConfigurers(List<WebMvcConfigurer> configurers) {

         if (!CollectionUtils.isEmpty(configurers)) {

             this.configurers.addWebMvcConfigurers(configurers);

            //一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;  

            @Override

            // public void addViewControllers(ViewControllerRegistry registry) {

             //    for (WebMvcConfigurer delegate : this.delegates) {

              //       delegate.addViewControllers(registry);

              //   }

             }

         }

}



3、容器中所有的WebMvcConfigurer都会一起起作用。

4、我们的配置类也会被调用。



效果:SpringMVC的自动配置和我们的扩展配置都会起作用。



3、全面接管SpringMVC

SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置,所有的SpringMVC的自动配置都失效了。



做法:我们需要在配置类中添加@EnableWebMvc即可。



@EnableWebMvc

@Configuration

public class MyMvcConfig implements WebMvcConfigurer {

    @Override

    public void addViewControllers(ViewControllerRegistry registry) {

        //浏览器发送 /keafmd 请求 来到success页面

        registry.addViewController("/keafmd").setViewName("success");

    }

}





全面接管后,静态资源失效。

不推荐这样全面接管。





原理

加了@EnableWebMvc自动配置就失效了。



1、@EnableWebMvc的核心:



@Import({DelegatingWebMvcConfiguration.class})

public @interface EnableWebMvc {



2、DelegatingWebMvcConfiguration



@Configuration(

    proxyBeanMethods = false

)

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {



3、WebMvcAutoConfiguration



@Configuration(

    proxyBeanMethods = false

)

@ConditionalOnWebApplication(

    type = Type.SERVLET

)

@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})

//容器中没有这个组件的时候,这个自动配置类才生效

@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

@AutoConfigureOrder(-2147483638)

@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})

public class WebMvcAutoConfiguration {



4、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来,自动配置类失效了。



5、导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能。



五、如何修改SpringBoot的默认配置

1、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来。

2、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置。

3、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置。



以上就是SpringBoot与Web开发(超详细)篇一的全部内容。

————————————————

版权声明:本文为CSDN博主「牛哄哄的柯南」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_43883917/article/details/114375472


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


CSS伪类:empty让我眼前一亮

前端达人

最近看过我文章的都知道:我最近在负责一个微信小程序的项目,在其中遇到了很多有趣的事和一些“奇思妙想”。本文的背景就是某天早上我看着wxml文件中一堆wx:if/elsehidden突然很烦躁,先不说wx:if导致的性能问题,就是标签上也是冗杂的。


接着上一篇文章【微信小程序自定义组件库yPicker组件分析及省市区三级联动实现】,在其中我分析了这么一个例子 —— 省市区三级联动的自定义实现,在其中有详细代码这里就不多说,说说如何调用:

我当时是这么想的:一方面出于“不在JavaScript里写太多东西”的考虑,另一方面,由于省、市、区我是分别用三个变量来实现的,所以JavaScript里就关注这三个变量,比如之间的空格或其它东西都拿到wxml文件里。就像这样:

<view class="departments location" bindtap="fixedshow"> <view class="depart_title">所在位置</view> <view wx:if="{{provinces&&citys&&areas}}" class="placeholder depart_content">{{provinces}} {{citys}} {{areas}}</view> <view class="placeholder depart_content befselect" wx:else>请选择当前位置</view> <view class="desc">如有变动请修改后再次提交</view> </view> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(因为调用涉及到后来改动的只有在点击弹窗里的“确认”按钮时在事件中将那三个变量分别赋给这段代码中出现的三个变量 —— 否则会只要改动不管是点取消还是确认已经发生改变了,这样不妥!)

其布局是这样的:

.departments{ width: 100%; height: 96rpx; display: flex; align-items: center; font-size: 36rpx; font-weight: 347; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .location{ position: relative; border-bottom: 1rpx solid rgba(0,0,0,.009); display: flex; align-items: flex-start; padding-top: 20rpx; } .desc{ position: absolute; right: 19rpx; bottom: 4rpx; color: rgb(63,142,255); font-size: 23rpx; } .departments .depart_title{ width: 20%; } .departments .depart_content{ margin-left: 10%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .departments .placeholder{ width: 69%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

location


在决定了要替换这里的wx:if以后,你首先要想:用什么替换?
wx:if作用是判断“是否存在”,如果不存在(条件不满足)就切换到wx:else或是wx:elif的逻辑里!

OK,想到这里,你应该能想到一个css伪类::empty !它的作用和我们想要的效果一样:判断如果元素(内容)为空的话…
我迅速对代码做了改动:

<view class="departments location" bindtap="fixedshow"> <view class="depart_title">所在位置</view> <view class="placeholder depart_content">{{provinces}} {{citys}} {{areas}}</view> <view class="desc">如有变动请修改后再次提交</view> </view> 
  • 1
  • 2
  • 3
  • 4
  • 5

然后在class - depart_content上加了这个伪类:

.placeholder:empty::before{ content: "请选择当前位置"; color: rgba(0,0,0,.6); } 
  • 1
  • 2
  • 3
  • 4

wx
一片空白!

经过查阅资料::empty伪类表示如果标签内容为空,那么内容区域如果带有空格,也是不会被匹配到的!

在写标签时一定要注意这一点:标签内是否有空格或换行!(换行常常被解析为一个空格)
遇到非单标签一定注意闭合标签!

最后解决办法是:在js中将三个变量用空格相连接,再渲染到页面上即可!
wx-position
(其实这里是一个自定义的选择器,而自动定位就是往高德地图发送了请求获取到省市区字段而已,代码就不写了。。。)


到这里我们会发现一个事:上面我们不仅用了empty伪类,还用了before伪元素!

其实这一点很平常 —— 毕竟只有empty是添加不了内容的(似乎纵观css,只有before和after这样伪元素可以向页面中添加内容,不管是文字还是图片之类的)

我认为更应该关注到的是两个地方:

  1. 伪元素中没有用position定位!一般来说对一个(存在内容的)元素来说,为其设置“前置”(before)/“后置”(after)样式都需要定位:规定其显示的地方。不然大概率伪元素中的文字是显示不出来的,通过本文的empty可以猜测:他被原本存在的内容覆盖住了。
  2. 从第一点可以得出::before 和 :after 伪元素向标签内插入内容、图形,并不会影响empty伪类的匹配!

这个特性实用的一批。


由上,可见此伪类最大的用处就是“字段缺失提示”!这是非常实用的。而且把这项任务交给CSS也可以减轻许多“(布局)负担”、体验更好、维护起来也更方便!

比如:我在项目优化时就将所有有请求的字段都加上了统一类名:

.ym-empty:empty::before{ content: "暂无数据,请重试", display: block; text-align: center; color: rgba(0,0,0,.6); /** 其它定位、字体更改操作 */ }


作者:,转载


炫酷的大数据可视化设计赏析(八)

前端达人

随着大数据产业的发展,越来越多的公司开始实现数据资源的管理和应用,尤其是一些在日常生活中经常使用大屏幕的大中型企业。此时,用户界面设计者需要呈现相应的视觉效果。


随着信息化的发展,智慧城市、智能油田、政务云、企业云等一系列信息化目标逐一实现。实现了以云平台为目标的各资源管控、资源业务的管理。随着管控触角的延伸、云存储的各种大数据,如何监控、分析、展示出核心数据和重点数据其尤为重要。

在集团企业中、以运维中心人员为用户群体,通过大屏实时掌握业务数据情况,在系统设计时既要考虑数据的直观性,又要考虑视觉疲劳,在其界面构思上要结合行业特点、数据特性进行策划,以立体感形式表现更佳。


接下来为大家介绍大屏可视化的UI设计。

jhk-1603104719964.jpg

jhk-1603104817863.jpg

jhk-1603866385802.jpg

jhk-1603866400790.jpg

jhk-1603866541134.jpg

jhk-1603866858741.jpg

jhk-1603866852818.jpg

jhk-1603866817588.jpg

jhk-1603866706529.jpg

jhk-1603866625088.jpg

jhk-1603866613876.jpg

jhk-1603866605141.jpg

jhk-1603866919828.png

jhk-1603866924130.jpg

jhk-1603866965540.jpg

jhk-1603867076475.jpg

jhk-1603867083483.jpg

jhk-1603867144749.jpg

jhk-1603867206051.jpg

WechatIMG565.png

WechatIMG607.jpeg



 --大屏UI设计--

--大数据可视化ui设计赏析--

(图片均来源于网络)

点击查看更多UI/UE设计案例⬇️⬇️⬇️

  蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服



    更多精彩文章:

       

 大数据可视化设计赏析(一)

  大数据可视化设计赏析(二)

  大数据可视化设计赏析(三)

  大数据可视化设计赏析(四)

  大数据可视化设计赏析(五)

  大数据可视化设计赏析(六)

  大数据可视化设计赏析(七)




JavaScript语法、语句、关键字保留字、变量

前端达人

第三章 基本概念

3.1 语法

3.1.1 区分大小写

  1. 变量名test和Test完全不同

3.1.2 标识符:变量、函数、属性的名字,或者函数的参数

  1. 命名规则
    • 第一个字符必须是一个字母、下划线、或者美元符号$
    • 其他字符可以是字母、下划线、美元符号、数字
  2. 采用驼峰大小写格式:第一个字母小写,剩下每个单词首字母大写。
    • for example:myName、herAge。
    • 驼峰式命名虽不是强制要求,但可以视为一种最佳实践。

3.1.3 注释

包括单行注释和块级注释。

  1. 单行注释:以两个斜杠开头。如下所示:
// alert(“HelloWorld!”) 
  • 1
  1. 块级注释:以一个斜杠和一个星号(/*)开头,以一个星号和一个斜杠结尾。如下所示:
/*
这是一个
多行的
块级注释
*/ 
  • 1
  • 2
  • 3
  • 4
  • 5

3.1.4 严格模式

  1. 定义:为JavaScript定义的一种不同的解析与执行模型。
  2. 使用方法:
    • 在整个脚本中启用严格模式,可以在顶部添加代码“use strict”;
    • 也可以在函数内部的上方包含这条编译指示。
  3. 使用效果:严格模式下,ECMAScript3中的一些不确定行为会得到处理,而且对某些不安全的操作也会抛出错误。严格模式下,js的执行效果会有很大不同。

3.1.5 语句

  • ECMAScript中的语句以一个分号结尾,但非必需。
  • 若省略分号,则由解析器确定语句的结尾。
  • 建议不要省略分号,因为写上解析器就不必要再花时间推测应该在哪里插入分号了。

3.2 关键字和保留字

ECMA-262描述了一组具有特定用途的关键字和一组不能用做标识符的保留字。

  1. 关键字:可以用于表示控制语句的开始或结束、或用于执行特定操作等。
  2. 保留字:保留字虽然在这门语言中还没有特定的用途,但他们有可能在将来被用作关键字。

3.3 变量

  • ECMAScript的变量是松散类型,即可以用来保存任何类型的数据。
  • 定义变量时要用var操作符,后跟变量名,例如var message,当然了,也可以直接在定义的时候对变量做一个初始化,例如var message = ‘hi’ ;
  • 这段的意思是变量message中保存了一个字符串“hi”。像这样初始化变量并不会把它标记为字符串类型,初始化的过程就只是给变量赋了一个值。
  • 因此,划重点,可以在修改变量的同时修改值的类型。例如:
var message = ‘hi’ ;
message = 100 ;   //有效,但不推荐
//这个例子代表变量message一开始保存了一个字符串“hi”,然后该值又被一个数字值100取代了。 
  • 1
  • 2
  • 3
  • 有一点需要注意,用var操作符定义的变量将成为该变量的作用域中的局部变量。也就是说如果在函数中使用var定义一个变量,那么这个变量在函数退出后就会被销毁。例如:
function test(){
    var  message = ‘hi’ ; //局部变量
} ;
test();
alert(message); //错误

//为什么是错误?
//这里,变量message是在函数里用var定义的,当函数被调用时,就会创建该变量并为其赋值。而在此之后,这个变量会立即被销毁。所以在执行alerat()那行代码的时候message已经被销毁了,因此报错。 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那么,该怎么解决呢?

  • 在函数内部省略掉var操作符,就可以创建一个全局变量,例子:
function test(){
    message = ‘hi’ ; //局部变量
} ;
test();
alert(message); // hi
//在函数内部不用var会创建全局变量。
//但我们并不提倡这种做法,因为局部作用域中定义的全局变量很难去维护。
//所以我们应该选择在开始就定义好所有的变量。

《跨域------>三种跨域底层分析》

前端达人

 转载:   原创作者: 

在这里插入图片描述

1.方式一:利用< script >标签,< script >不存在跨域问题


在这里插入图片描述

2. (服务端解决)SpringMVC的controller中加入注解@CrossOrigin(value = “http://localhost:8080”)

即:给响应头设置一个Access-Control-Allow-Origin属性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.(客户端解决)使用Ajax中的JSONP实现跨域(常用)

3.1原理:利用< script >标签

3.2代码

将callback当成一个参数传递下去在这里插入图片描述
在这里插入图片描述
前后端运行结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

深入理解vue中的slot与slot-scope

前端达人

写在前面

vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项使用频率、使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧,回头再学,反正已经可以写基础组件了”,于是就关闭了vue说明文档。

实际上,插槽的概念很简单,下面通过分三部分来讲。这个部分也是按照vue说明文档的顺序来写的。

进入三部分之前,先让还没接触过插槽的同学对什么是插槽有一个简单的概念:插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot最核心的两个问题这里就点出来了,是显示不显示怎样显示

由于插槽是一块模板,所以,对于任何一个组件,从模板种类的角度来分,其实都可以分为非插槽模板插槽模板两大类。
非插槽模板指的是html模板,指的是‘div、span、ul、table’这些,非插槽模板的显示与隐藏以及怎样显示由插件自身控制;插槽模板是slot,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot写在组件template的哪块,父组件传过来的模板将来就显示在哪块

单个插槽 | 默认插槽 | 匿名插槽

首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。

单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。

下面通过一个例子来展示。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. </child>
  14. </div>
  15. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. <slot></slot>
  5. </div>
  6. </template>

在这个例子里,因为父组件在<child></child>里面写了html模板,那么子组件的匿名插槽这块模板就是下面这样。也就是说,子组件的匿名插槽被使用了,是被下面这块模板使用了。


  1. <div class="tmpl">
  2. <span>菜单1</span>
  3. <span>菜单2</span>
  4. <span>菜单3</span>
  5. <span>菜单4</span>
  6. <span>菜单5</span>
  7. <span>菜单6</span>
  8. </div>

最终的渲染结果如图所示:



  1. 注:所有demo都加了样式,以方便观察。其中,父组件以灰色背景填充,子组件都以浅蓝色填充。

具名插槽

匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次。出现在不同的位置。下面的例子,就是一个有两个具名插槽单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl" slot="up">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. <div class="tmpl" slot="down">
  14. <span>菜单-1</span>
  15. <span>菜单-2</span>
  16. <span>菜单-3</span>
  17. <span>菜单-4</span>
  18. <span>菜单-5</span>
  19. <span>菜单-6</span>
  20. </div>
  21. <div class="tmpl">
  22. <span>菜单->1</span>
  23. <span>菜单->2</span>
  24. <span>菜单->3</span>
  25. <span>菜单->4</span>
  26. <span>菜单->5</span>
  27. <span>菜单->6</span>
  28. </div>
  29. </child>
  30. </div>
  31. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. // 具名插槽
  4. <slot name="up"></slot>
  5. <h3>这里是子组件</h3>
  6. // 具名插槽
  7. <slot name="down"></slot>
  8. // 匿名插槽
  9. <slot></slot>
  10. </div>
  11. </template>

显示结果如图:


可以看到,父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

作用域插槽 | 带数据的插槽

最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写


  1. 匿名插槽
  2. <slot></slot>
  3. 具名插槽
  4. <slot name="up"></slot>

但是作用域插槽要求,在slot上面绑定数据。也就是你得写成大概下面这个样子。


  1. <slot name="up" :data="data"></slot>
  2. export default {
  3. data: function(){
  4. return {
  5. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  6. }
  7. },
  8. }

我们前面说了,插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样。


  1. <child>
  2. html模板
  3. </child>

写了,插槽就总得在浏览器上显示点东西,东西就是html该有的模样,没写,插槽就是空壳子,啥都没有。
OK,我们说有html模板的情况,就是父组件会往子组件插模板的情况,那到底插一套什么样的样式呢,这由父组件的html+css共同决定,但是这套样式里面的内容呢?

正因为作用域插槽绑定了一套数据,父组件可以拿来用。于是,情况就变成了这样:样式父组件说了算,但内容可以显示子组件插槽绑定的。

我们再来对比,作用域插槽和单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件是提供的模板要既包括样式由包括内容的,上面的例子中,你看到的文字,“菜单1”,“菜单2”都是父组件自己提供的内容;而作用域插槽,父组件只需要提供一套样式(在确实用作用域插槽绑定的数据的前提下)。

下面的例子,你就能看到,父组件提供了三种样式(分别是flex、ul、直接显示),都没有提供数据,数据使用的都是子组件插槽自己绑定的那个人名数组。

父组件:


  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <!--第一次使用:用flex展示数据-->
  5. <child>
  6. <template slot-scope="user">
  7. <div class="tmpl">
  8. <span v-for="item in user.data">{{item}}</span>
  9. </div>
  10. </template>
  11. </child>
  12. <!--第二次使用:用列表展示数据-->
  13. <child>
  14. <template slot-scope="user">
  15. <ul>
  16. <li v-for="item in user.data">{{item}}</li>
  17. </ul>
  18. </template>
  19. </child>
  20. <!--第三次使用:直接显示数据-->
  21. <child>
  22. <template slot-scope="user">
  23. {{user.data}}
  24. </template>
  25. </child>
  26. <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
  27. <child>
  28. 我就是模板
  29. </child>
  30. </div>
  31. </template>

子组件:


  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. // 作用域插槽
  5. <slot :data="data"></slot>
  6. </div>
  7. </template>
  8. export default {
  9. data: function(){
  10. return {
  11. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  12. }
  13. }
  14. }

结果如图所示:


转载地址:https://segmentfault.com/a/1190000012996217

转载作者/云荒杯倾

CSS基础知识第三篇

前端达人

盒子模型

看透网页布局的本质

首先利用CSS设置好盒子的大小,然后摆放盒子的位置。最后把网页元素比如文字图片等等,放入盒子里面。

概念

盒子模型由元素的内容边框border内边距padding和外边距margin组成
盒子里面的文字和图片等元素是内容区域,盒子的厚度我们称为盒子的边框
盒子内容与边框的距离是内边距,盒子与盒子之间的距离是外边距

盒子边框

语法

border:border-width粗细|border-style样式|border-color颜色 

边框综合设置
border: 1px solid red; 没有顺序

表格的细线边框
cellspacing=“0” ,将单元格与单元格之间的距离设置为0
border-collapse:collapse; 表示相邻边框合并在一起

内边距

padding属性用于设置内边距,是指边框与内容之间的距离

属性
padding-left左内边距padding-right右内边距padding-top上内边距padding-bottom下内边距

简写
2个值 padding: 上下内边距 左右内边距 ;
4个值 padding: 上内边距 右内边距 下内边距 左内边距 ;

内盒尺寸计算(元素实际大小)
盒子的实际的大小 = 内容的宽度和高度 + 内边距 + 边框

外边距

margin属性用于设置外边距。margin控制盒子和盒子之间的距离,属性和简写与padding相同

块级盒子水平居中

盒子必须指定宽度(width)然后就给左右的外边距都设置为auto

文字居中和盒子居中区别

盒子内的文字水平居中是text-align:center, 而且还可以让行内元素和行内块居中对齐
块级盒子水平居中 左右margin 改为 auto

清除元素的默认内外边距

代码

* {
padding:0;     /* 清除内边距 */
margin:0;      /* 清除外边距 */
} 

注意
行内元素为了兼容性, 尽量只设置左右内外边距, 不设置上下内外边距

外边距合并

相邻块元素垂直外边距的合并

当上下相邻的两个块元素相遇时,如果上面的元素有下外边距margin-bottom,下面的元素有上外边距margin-top,则他们之间的垂直间距不是margin-bottom与margin-top之和,取两个值中的较大者这种现象被称为相邻块元素垂直外边距的合并

解决方案
尽量给只给一个盒子添加margin值

嵌套块元素垂直外边距的合并

对于两个嵌套关系的块元素,如果父元素没有上内边距及边框,父元素的上外边距会与子元素的上外边距发生合并,合并后的外边距为两者中的较大者

解决方案
可以为父元素定义上边框
可以为父元素定义上内边距
可以为父元素添加overflow:hidden

盒子模型布局稳定性

按照优先使用宽度(width)内边距(padding)外边距(margin)

原因
margin有外边距合并还有ie6下面margin加倍的bug所以最后使用
padding会影响盒子大小,需要进行加减计算,其次使用
width没有问题,经常使用宽度剩余法高度剩余法来做

浮动

CSS 布局的三种机制

普通流(标准流)

块级元素会独占一行,从上向下顺序排列
行内元素会按照从左到右顺序排列,碰到父元素边缘则自动换行

浮动

让盒子从普通流中浮起来,主要作用让多个块级盒子一行显示

定位

将盒子定在浏览器的某一个位置

为什么需要浮动

因为行内块元素可以实现多个元素一行显示但中间会有空白缝隙
因为行内块元素不能实现盒子左右对齐

什么是浮动

概念

元素的浮动是指设置了浮动属性的元素
会脱离标准普通流的控制并可以移动到指定位置

作用

让多个盒子(div)水平排列成一行,使得浮动成为布局的重要手段
浮动最早是用来控制图片,实现文字环绕图片的效果
可以实现盒子的左右对齐等等

语法

选择器 { float: 属性值; } 
  • 1

属性值
none(元素不浮动(默认))left(元素左浮动)right(右浮动)

特点


加了浮动的盒子是浮起来的,漂浮在其他标准流盒子的上面

加了浮动的盒子不占位置,它原来的位置漏给了标准流的盒子

浮动元素改变display属性, 类似转换成行内块元素,但是元素之间没有空白缝隙

浮动的应用

浮动和标准流的父盒子搭配

实际的导航栏中不直接用链接a而是用li包含链接(li+a)
li+a语义更清晰
直接用a搜索引擎容易辨别为有堆砌关键字嫌疑而影响网站排名

浮动的扩展

浮动元素与父盒子的关系
子盒子的浮动参照父盒子对齐
不会与父盒子的边框重叠,也不会超过父盒子的内边距

浮动元素与兄弟盒子的关系
在一个同一个父级盒子中,如果前一个兄弟盒子是浮动的,那么当前盒子会与前一个盒子的顶部对齐
在一个同一个父级盒子中,如果前一个兄弟盒子是普通流的,那么当前盒子会显示在前一个兄弟盒子的下方

清除浮动

为什么要清除浮动

浮动元素不占用原文档流的位置,会对后面的元素排版产生影响

清除浮动本质

父级元素因为子级浮动导致内部高度为0,清除浮动后,父级会根据浮动的子盒子检测高度,父级有高度就不会影响下面的标准流

清除浮动的方法

语法

选择器{clear:属性值;}  clear 清除 
  • 1

属性值
left清除左浮动right清除右浮动both同时清除左右浮动

额外标签法

是W3C推荐的做法是通过在浮动元素末尾添加一个空的标签例如<div style=”clear:both”></div>,或则其他标签br等亦可 
  • 1

优缺点
通俗易懂,书写方便,但是添加许多无意义的标签,结构化较差

父级添加overflow属性方法

可以给父级添加:overflow为hidden|auto|scroll都可以实现

优缺点
代码简洁,但是内容增多时候容易造成不会自动换行导致内容被隐藏掉,无法显示需要溢出的元素

使用after伪元素清除浮动

:after 方式为空元素额外标签法的升级版,.clearfix:after {  content: ""; display: block; height: 0; clear: both;visibility: hidden; } 
.clearfix {*zoom: 1;}  /* IE6、7 专有 */ 
  • 1
  • 2

优缺点
符合闭合浮动思想结构语义化正确,但是由于IE6-7不支持:after,使用zoom:1触发hasLayout

使用双伪元素清除浮动

方法

.clearfix:before,.clearfix:after {
content:"";
display:table;
}
.clearfix:after {
clear:both;
}
.clearfix {
*zoom:1;
} 

优缺点
代码更简洁,但由于IE6-7不支持:after使用zoom:1触发hasLayout

ES6 的循环和可迭代对象

seo达人

本文将研究 ES6 的 for ... of 循环。

旧方法

在过去,有两种方法可以遍历 javascript。

首先是经典的 for i 循环,它使你可以遍历数组或可索引的且有 length 属性的任何对象。

for(i=0;i<things.length;i++) { var thing = things[i] /* ... */ }

其次是 for ... in 循环,用于循环一个对象的键/值对。

for(key in things) { if(!thing.hasOwnProperty(key)) { continue; } var thing = things[key] /* ... */ }

for ... in 循环通常被视作旁白,因为它循环了对象的每一个可枚举属性。这包括原型链中父对象的属性,以及被分配为方法的所以属性。换句话说,它遍历了一些人们可能想不到的东西。使用 for ... in 通常意味着循环块中有很多保护子句,以避免出现不需要的属性。

早期的 javascript 通过库解决了这个问题。许多 JavaScript库(例如:Prototype.js,jQuery,lodash 等)都有类似 each 或 foreach 这样的工具方法或函数,可让你无需 for i 或 for ... in 循环去遍历对象和数组。

for ... of 循环是 ES6 试图不用第三方库去解决其中一些问题的方式。

for … of

for ... of 循环

for(const thing of things) { /* ... */ }

它将遍历一个可迭代(iterable)对象。

可迭代对象是定义了 @@ iterator 方法的对象,而且 @@iterator 方法返回一个实现了迭代器协议的对象,或者该方法是生成器函数。

在这句话中你需要理解很多东西:

  • 可迭代的对象
  • @@iterator方法( @@是什么意思?)
  • 迭代器协议(这里的协议是什么意思?)
  • 等等,迭代(iterable)和迭代器(iterator)不是一回事?
  • 另外,生成器函数又是什么鬼?

下面逐个解决这些疑问。

内置 Iterable

首先,javascript 对象中的一些内置对象天然的可以迭代,比如最容易想到的就是数组对象。可以像下面的代码中一样在 for ... of 循环中使用数组:

const foo = [ 'apples','oranges','pears' ] for(const thing of foo) { console.log(thing)
}

输出结果是数组中的所有元素。

apples oranges
pears

还有数组的 entries 方法,它返回一个可迭代对象。这个可迭代对象在每次循环中返回键和值。例如下面的代码:

const foo = [ 'apples','oranges','pears' ] for(const thing of foo.entries()) { console.log(thing)
}

将输出以下内容

[ 0, 'apples' ]
[ 1, 'oranges' ]
[ 2, 'pears' ]

当用下面的语法时,entries 方法会更有用

const foo = [ 'apples','oranges','pears' ] for(const [key, value] of foo.entries()) { console.log(key,':',value)
}

在 for 循环中声明了两个变量:一个用于返回数组的第一项(值的键或索引),另一个用于第二项(该索引实际对应的值)。

一个普通的 javascript 对象是不可迭代的。如果你执行下面这段代码:

// 无法正常执行 const foo = { 'apples':'oranges', 'pears':'prunes' } for(const [key, value] of foo)

{ console.log(key,':',value)

}

会得到一个错误

$ node test.js
/path/to/test.js:6 for(const [key, value] of foo) {
TypeError: foo is not iterable

然而全局 Object 对象的静态 entries 方法接受一个普通对象作为参数,并返回一个可迭代对象。就像这样的程序:

const foo = { 'apples':'oranges', 'pears':'prunes' } for(const [key, value] of Object.entries(foo))

{ console.log(key,':',value)

}

能够得到你期望的输出:

$ node test.js
apples : oranges
pears : prunes

创建自己的 Iterable

如果你想创建自己的可迭代对象,则需要花费更多的时间。你会记得前面说过:

可迭代对象是定义了 @@ iterator 方法的对象,而且 @@iterator 方法返回一个实现了迭代器协议的对象,或者该方法是生成器函数。

搞懂这些内容的最简单方法就是一步一步的去创建可迭代对象。首先,我们需要一个实现 @@iterator 方法的对象。 @@ 表示法有点误导性,我们真正要做的是用预定义的 Symbol.iterator 符号定义方法。

如果用迭代器方法定义对象并尝试遍历:

const foo = {
  [Symbol.iterator]: function() {
  }
} for(const [key, value] of foo) { console.log(key, value)
}

得到一个新错误:

for(const [key, value] of foo) {
                          ^
TypeError: Result of the Symbol.iterator method is not an object

这是 javascript 告诉我们它在试图调用 Symbol.iterator 方法,但是调用的结果不是对象。

为了消除这个错误,需要用迭代器方法来返回实现了迭代器协议的对象。这意味着迭代器方法需要返回一个有 next 键的对象,而 next 键是一个函数。

const foo = {
  [Symbol.iterator]: function() { return { next: function() {
      }
    }
  }
} for(const [key, value] of foo) { console.log(key, value)
}

如果运行上面的代码,则会出现新错误。

for(const [key, value] of foo) {
                     ^
TypeError: Iterator result undefined is not an object

这次 javascript 告诉我们它试图调用 Symbol.iterator 方法,而该对象的确是一个对象,并且实现了 next 方法,但是 next 的返回值不是 javascript 预期的对象。

next 函数需要返回有特定格式的对象——有 value 和 done 这两个键。

next: function() { //... return { done: false, value: 'next value' }
}

done 键是可选的。如果值为 true(表示迭代器已完成迭代),则说明迭代已结束。

如果 done 为 false 或不存在,则需要 value 键。 value 键是通过循环此应该返回的值。

所以在代码中放入另一个程序,它带有一个简单的迭代器,该迭代器返回前十个偶数。

class First20Evens { constructor() { this.currentValue = 0 }

  [Symbol.iterator]() { return { next: (function() { this.currentValue+=2 if(this.currentValue > 20) { return {done:true}
        } return { value:this.currentValue
        }
      }).bind(this)
    }
  }
} const foo = new First20Evens; for(const value of foo) { console.log(value)
}

生成器

手动去构建实现迭代器协议的对象不是唯一的选择。生成器对象(由生成器函数返回)也实现了迭代器协议。上面的例子用生成器构建的话看起来像这样:

class First20Evens { constructor() { this.currentValue = 0 }

  [Symbol.iterator]() { return function*() { for(let i=1;i<=10;i++) { if(i % 2 === 0) { yield i
        }
      }
    }()
  }
} const foo = new First20Evens; for(const item of foo) { console.log(item)
}

本文不会过多地介绍生成器,如果你需要入门的话可以看这篇文章。今天的重要收获是,我们可以使自己的 Symbol.iterator 方法返回一个生成器对象,并且该生成器对象能够在 for ... of 循环中“正常工作”。 “正常工作”是指循环能够持续的在生成器上调用 next,直到生成器停止 yield 值为止。

$ node sample-program.js
2
4
6
8 

10

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

Fluter 应用调试

seo达人

Flutter 构建模式

目前,Flutter一共提供了三种运行模式,分别是Debug、Release和Profile模式。其中,Debug模式主要用在软件编写过程中,Release模式主要用于应用发布过程中,而Profile模式则主要用于应用性能分析时,每个模式都有自己特殊的使用场景。下面简介介绍下这几种模式:


Debug模式

Debug模式又名调试模式,Debug模式可以同时在物理设备、仿真器或者模拟器上运行应用。默认情况下,使用flutter run命令运行应用程序时就是使用的Debug模式。在Debug模式下,所有的断言、服务扩展是开启的,并且在模式对快速开发和运行周期进行了编译优化,当使用调试工具进行代码调试时可以直接连接到应用的进程里。


Release模式

Release模式又名发布模式,此模式只能在物理设备上运行,不能在模拟器上运行。使用flutter run --release命令运行应用程序时就是使用的Release模式。在Release模式下,断点、调试信息和服务扩展是不可用的,并且Release模式针对快速启动、快速执行和安装包大小进行了优化。


Profile模式

Profile模式只能在物理设备上运行,不能在模拟器上运行。此模式主要用于应用性能分析,一些应用调试能力是被保留的,目的是分析应用存在的性能问题。Profile模式和Release模式大体相同,不同点体现在,Profile模式的某些服务扩展是启用的,某些进程调试手段也是开启的。


调试模式

在 Debug 模式下,app 可以被安装在物理设备、仿真器或者模拟器上进行调试。在Debug模式下,可以进行如下操作:


断点 是开启的。

服务扩展是开启的。

针对快速开发和运行周期进行了编译优化(但不是针对执行速度、二进制文件大小或者部署)。

调试开启,类似 开发者工具 等调试工具可以连接到进程里。

如果是在 Web 平台下的调试模式,可以进行如下操作:


本次构建 没有 最小化资源并且整个构建 没有 优化性能。

为了简化调试,这个 Web 应用使用了 dartdevc 编译器。

默认情况下,运行 flutter run 会使用 Debug 模式,同时 IDE 也支持这些模式。例如,Android Studio 提供了 Run > Debug… 菜单选项,而且在项目面板中还有一个三角形的绿色运行按钮图标 。


Release 模式

当你想要最大的优化以及最小的占用空间时,就使用 Release 模式来部署 app。 release 模式是不支持模拟器或者仿真器的,使用 Release 模式意味着。


断点是不可用的。

调试信息是不可见的。

调试是禁用的。

编译针对快速启动、快速执行和小的 package 的大小进行了优化。

服务扩展是禁用的。

对于Web开发来说,使用 Release 模式意味着。


这次构建资源已经被压缩,并且性能得以优化。

这个 Web 应用通过 dart2js 编译器构建,以确保更优秀的性能。

Profile 模式

在 Profile 模式下,一些调试能力是被保留的,足够分析你的 app 性能。Profile 模式在仿真器和模拟器上是不可用的,因为他们的行为不能代表真实的性能。和 release 相比, profile 模式有以下不同:


一些服务扩展是启用的。例如,支持 performance overlay。

Tracing 是启用的,一些调试工具,比如 开发者工具 可以连接到进程里。

在 Web 平台使用Profile 模式意味着:


资源文件没有被压缩,但是整体性能已经优化。

这个 Web 应用通过 dart2js 编译器构建。

调试工具

在Flutter应用开发中,有很多工具可以帮助调试 Flutter 应用程序,常见的如下所示。


开发者工具,是一套运行在浏览器的性能及分析工具。

Android Studio/IntelliJ 和 VS Code(借助 Flutter 和 Dart 插件)支持内置的源代码调试器,可以设置断点,单步调试,检查数值。

Flutter inspector,是开发者工具提供的 widget 检查器,也可直接在 Android Studio 和 IntelliJ 中使用(借助 Flutter 插件)。检查器可以可视化展现 widget 树,查看单个 widget 及其属性值,开启性能图层,等等。

开发者工具

要调试及分析应用,开发者工具可能是你的首选。开发者工具运行在浏览器,支持以下特性:


源代码调试器

Widget 检查器,展示可视化的 widget 树; “widget select” 模式,在应用中选择一个 widget,会在 widget 树直接定位到它的位置。

内存分析

时间线视图,支持跟踪,导入及导出跟踪信息

日志视图

如果你在Debug 模式 或Profile 模式 运行,那么可以在浏览器打开开发者工具连接到你的应用。开发者工具不能用在 Release 模式 编译的应用,因为调试和分析信息都被删除了。如果你要用开发者工具分析应用,需确保使用 Profile 模式运行应用。


在这里插入图片描述


断点调试

和其他语言一样,Flutter的断点调试支持在 IDE 或编辑器(比如 Android Studio/IntelliJ 和 VS Code)、或者通过编码两种方式。

其中,开发者工具调试器如下图所示。

在这里插入图片描述


如果需要,在源代码中设置断点,然后点击工具栏中的 【Debug】 按钮,或选择 【Run】 > 【Debug】即可开启调试功能。

在这里插入图片描述


开启调试后,可以在控制台看到如下一些信息。


底部的 Debugger 窗口会显示出堆栈和变量信息。

底部的 Console 窗口会显示详细的日志输出。

调试基于默认的启动配置,如果需要自定义,点击选择目标下拉按钮,选择 Edit configuration 进行配置。

在进行断点调试时,使用得最多的就是单步调试,三个单步调试按钮在暂停后会变为可用状态。


使用 Step in 来进入被调用的方法,在遇到方法内的第一行可执行代码时结束。

使用 Step over 直接执行某个方法调用而不进入内部;该按钮在当前方法内按行执行。

使用 Step out 来跳出当前方法,这种方式会直接执行完所有当前方法内的语句。

除此之外,我们还可以使用代码的方式进行断点调试,我们可以在源代码中使用 debugger()函数来开启断点,当代码运行到此处时就会刮起,如下所示。


import 'dart:developer';


void someFunction(double offset) {

 debugger(when: offset > 30.0);

 // ...

}

Dart 分析器

如果你使用的是 Android Studio或者VSCode,那么工具会自带的 Dart 分析器默认会检查代码,并发现可能的错误。如果你使用命令行,则可以使用 flutter analyze命令来检查代码。Dart 分析器非常依赖你在代码中添加的类型注解,以帮助跟踪问题。


另外,我们可以使用flutter analyze --flutter-repo命令将分析结果打印到控制台上,每次运行这个命名之前,请先运行flutter update-packages 升级的包,这样就可以获取的依赖包。如果你不这样做,你可能会从dart:ui得到一些错误消息,比如偏移量等。因为执行flutter analysis 命令时并不会主动去拉取依赖。


对于一次性的Dart分析,直接使用flutter analyze --flutter-repo即可,对于连续分析,则可以使用flutter analyze --flutter-repo --watch命令。如果你想知道多少个成员变量丢失了dartdocs,可以添加一个dartdocs参数。


Flutter inspector 工具

Flutter inspector 是分析Flutter组件状态树的利器,Flutter使用小部件来控制页面组件到布局的精准控制,Flutter inspector 可以帮助我们进行如下一些分析。


进行布局分析,理解布局层次

诊断布局问题

在这里插入图片描述


在调试模式下,我们点击Android Studio右边Flutter inspector按钮即可开启Flutter inspector分析,Flutter inspector提供了如下的可视化调试工具。

在这里插入图片描述


Select widget mode:启用此按钮后,选择组件树的代码会自动跳转到对应的源代码里面。

Refresh tree : 重新加载的组件信息。

Slow Animations:放慢动画速度,以便进行视觉上的查验。

Debug Paint: 边框、方向的可视化。

Paint Baselines: 每个渲染框在它的每个文本基线上画一条线。

Repaint Rainbow:查看重绘的严重程度,严重的会被爆红。

除了上面的功能外,我们还可以点击【Open DevTools】打开Flutter的调试页面,可以借助它进行很多性能分析,后面会具体介绍。

在这里插入图片描述


测量应用启动时间

要收集有关 Flutter 应用程序启动所需时间的详细信息,可以在运行 flutter run 命令时使用 trace-startup 和 profile 选项,如下所示。


flutter run --trace-startup --profile

跟踪输出被保存到 Flutter 工程目录在 build 目录下,一个名为 start_up_info.json 的 JSON 文件中,输出列出了从应用程序启动到这些跟踪事件(以微秒捕获)所用的时间,如下所示。


{

 "engineEnterTimestampMicros": 2346054348633,

 "timeToFrameworkInitMicros": 812748,

 "timeToFirstFrameRasterizedMicros": 1573154,

 "timeToFirstFrameMicros": 1221472,

 "timeAfterFrameworkInitMicros": 408724

}

对应的具体含义如下:


进入 Flutter 引擎时

展示应用第一帧时

初始化Flutter框架时

完成Flutter框架初始化时

使用Android Studio进行调试

Flutter官方推荐使用Android Studio或VSCode进行应用开发, 和其他语言的调试一样,Dart代码的调试流程也差不多。如果还没有Flutter项目,可以新建一个示例项目。通过单击首先,点击调试图标(Debug-run icon)同时打开调试面板并在控制台中运行应用,首次运行应用是最慢的,应用启动后,界面应该是下面这样的。

在这里插入图片描述


然后,我们在在 counter++ 这一行上添加断点。在应用里,点击 + 按钮(FloatingActionButton,或者简称 FAB)来增加数字,应用会暂停。

在这里插入图片描述


你可以 step in/out/over Dart 语句、热重载和恢复执行应用、以及像使用其他调试器一样来使用 Dart 调试器。


Flutter inspector

Flutter inspector 是一个用来可视化以及查看 Flutter widget 树的工具,提供如下功能:


了解现有布局

诊断布局问题

可以使用 Android Studio 窗口右侧的垂直按钮来打开Flutter inspector,如下图所示。


在这里插入图片描述


Flutter outline

Flutter Outline 是一个可视的显示页面构建方法的功能,注意在构建方法上可能与 widget 树不同,可以使用 Android Studio 窗口右侧的垂直按钮切换 outline 的显示。

在这里插入图片描述


Tip: 我们可以安装一个 Presentation Assistant 插件来辅助我们进行开发,Presentation Assistant 提供了很多的快捷功能。例如,当焦点在编辑面板中时,输入 command-Shift-A(Mac)或者 shift-control-A(Windows 和 Linux),该插件会同时显示「查找」面板并显示在所有三个平台上执行此操作的提示。

在这里插入图片描述


然后在输入框中输入attach关键字,显示如下图。


在这里插入图片描述


使用 Android Gradle 调试

为了调试原生代码,你需要一个包含 Android 原生代码的应用。在本节中,你将学会如何连接两个调试器到你的应用:

1)Dart 调试器。

2)Android Gradle 调试器。


创建一个基本的 Flutter 应用,然后替换 lib/main.dart 的代码为以下示例代码。


// Copyright 2017 The Chromium Authors. All rights reserved.

// Use of this source code is governed by a BSD-style license that can be

// found in the LICENSE file.


import 'dart:async';


import 'package:flutter/material.dart';

import 'package:url_launcher/url_launcher.dart';


void main() {

 runApp(MyApp());

}


class MyApp extends StatelessWidget {

 @override

 Widget build(BuildContext context) {

   return MaterialApp(

     title: 'URL Launcher',

     theme: ThemeData(

       primarySwatch: Colors.blue,

     ),

     home: MyHomePage(title: 'URL Launcher'),

   );

 }

}


class MyHomePage extends StatefulWidget {

 MyHomePage({Key key, this.title}) : super(key: key);

 final String title;


 @override

 _MyHomePageState createState() => _MyHomePageState();

}


class _MyHomePageState extends State<MyHomePage> {

 Future<void> _launched;


 Future<void> _launchInBrowser(String url) async {

   if (await canLaunch(url)) {

     await launch(url, forceSafariVC: false, forceWebView: false);

   } else {

     throw 'Could not launch $url';

   }

 }


 Future<void> _launchInWebViewOrVC(String url) async {

   if (await canLaunch(url)) {

     await launch(url, forceSafariVC: true, forceWebView: true);

   } else {

     throw 'Could not launch $url';

   }

 }


 Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {

   if (snapshot.hasError) {

     return Text('Error: ${snapshot.error}');

   } else {

     return Text('');

   }

 }


 @override

 Widget build(BuildContext context) {

   String toLaunch = 'https://flutter.dev';

   return Scaffold(

     appBar: AppBar(

       title: Text(widget.title),

     ),

     body: Center(

       child: Column(

         mainAxisAlignment: MainAxisAlignment.center,

         children: <Widget>[

           Padding(

             padding: EdgeInsets.all(16.0),

             child: Text(toLaunch),

           ),

           RaisedButton(

             onPressed: () => setState(() {

                   _launched = _launchInBrowser(toLaunch);

                 }),

             child: Text('Launch in browser'),

           ),

           Padding(padding: EdgeInsets.all(16.0)),

           RaisedButton(

             onPressed: () => setState(() {

                   _launched = _launchInWebViewOrVC(toLaunch);

                 }),

             child: Text('Launch in app'),

           ),

           Padding(padding: EdgeInsets.all(16.0)),

           FutureBuilder<void>(future: _launched, builder: _launchStatus),

         ],

       ),

     ),

   );

 }

}

然后,添加 url_launcher 依赖到 pubspec 文件,并执行 flutter pub get命令拉取依赖包。


name: flutter_app

description: A new Flutter application.

version: 1.0.0+1


dependencies:

 flutter:

   sdk: flutter


 url_launcher: ^3.0.3

 cupertino_icons: ^0.1.2


dev_dependencies:

 flutter_test:

   sdk: flutter

点击调试按钮(Debug-run icon)来同时打开调试面板并启动应用,如下图所示。

在这里插入图片描述


点击 【Attach debugger to Android process】 按钮,从进程对话框中,你应该可以看到每一个设备的入口。选择 show all processes 来显示每个设备可用的进程。

在这里插入图片描述


在调试面板中,你现在应该可以看到一个 Android Debugger 标签页,然后依次选择【app_name】 > 【android】 > 【app】 > 【src】 >【 main】 > 【java】 > 【io.flutter plugins】在项目面板,然后双击 GeneratedProjectRegistrant 在编辑面板中打开 Java 代码,此时Dart 和原生调试器都在与同一个进程交互。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

基于vue脚手架构建库并发布到npm

seo达人

构建库的常见方法有两种:一种是自己手动构建webpack库打包,设置output为 library; 另一种是基于vue-cli3输出库资源包。我们采用第二种vue脚手架的方式构建库。


新增编译库命令

// package.json

"scripts": {

   // ...

   "lib": "vue-cli-service build --target lib --name Step --dest dist packages/index.js"

}


// packages/index.js  默认打包Step

import Step from '../steps/src/step';

Step.install = function(Vue) {

   Vue.component(Step.name, Step);

};

export default Step;

--name: 库名称。

--target: 构建目标,默认为应用模式。这里修改为 lib 启用库模式。

--dest: 输出目录,默认 dist。

[entry]: 最后一个参数为入口文件,默认为 src/App.vue。

更多详细配置查看 ☛ vue脚手架官网


如果该库依赖于其他库,请在vue.config.js 配置 externals

// vue.config.js

module.exports = {

   configureWebpack:{

     externals: {

        vue: 'Vue',

        'vue-router':'VueRouter',

        axios: 'axios'

     }

   }

}

执行 npm run lib 就可以发现我们的库被打包到了 根目录的dist文件夹下。


添加 .npmignore 文件(可选)

和 .gitignore 的语法一样,具体需要提交什么文件,看各自的实际情况


# 忽略目录

examples/

packages/

public/


# 忽略指定文件

vue.config.js

babel.config.js

*.map

配置npm库信息

配置package.json文件,以发布库文件。


{

 "name": "gis",

 "version": "1.2.5",

 "description": "基于 Vue 的库文件",

 "main": "dist/gis.umd.min.js",

 "keyword": "vue gis",

 "private": false,

  "files": ["dist"],

 "license": "MIT"

}

name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。

version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。

description: 描述。

main: 入口文件,该字段需指向我们最终编译后的包文件。

keyword:关键字,以空格分离希望用户最终搜索的词。

author:作者

files: 要上传的文件

private:是否私有,需要修改为 false 才能发布到 npm

license: 开源协议

dependencies: 依赖库

注意每次发布新的库,需要更改版本号,规则如下:

"version": "1.2.5" 主版本号为 1,次版本号 2,修订号 5

主版本号(Major):当你做了不兼容的API修改

次版本号(Minor):当你做了向下兼容的功能性新增

修订号(Patch):当你做了向下兼容的问题修正

登录npm

首先设置登录的npm镜像地址


npm config set registry http://168.20.20.57.4873

然后在终端执行登录命令,输入用户名、密码、邮箱即可登录


npm login

接着发布库资源到npm


npm publish

最后发布成功可到官网查看对应的包并下载


npm install package_name

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档